Android - plotting gps coordinate on custom map

2019-03-27 13:44发布

问题:

I have an activity in my app where there are images I use as maps. If the image is "grid aligned" to google maps then when I use the top left and bottom right corners of the map I get from googlemaps online then I can turn the users gps into an x and y on screen. However if the map is not "grid aligned" and is at an angle than the values my math returns draws the users position off screen. Obviously I am missing the part on how to handle the angle of the map so if anyone could shed some light on how to do that it would be super helpfull.

I know I would have to figure out the angle in radians and do some conversion but I have no idea where to start. This is what I use to get an x and y to plot on a canvas so far.

public double[] getPositionGPS(Location upperLeft, Location lowerRight, Location current){

    try{

        double hypotenuse = upperLeft.distanceTo(current);
        double bearing = upperLeft.bearingTo(current);
        double currentDistanceY = Math.cos(bearing * Math.PI / OneEightyDeg) * hypotenuse;
        //                           "percentage to mark the position"
        double totalHypotenuse = upperLeft.distanceTo(lowerRight);
        double totalDistanceY = totalHypotenuse * Math.cos(upperLeft.bearingTo(lowerRight) * Math.PI / OneEightyDeg);
        double currentPixelY = currentDistanceY / totalDistanceY * ImageSizeH;

        double hypotenuse2 = upperLeft.distanceTo(current);
        double bearing2 = upperLeft.bearingTo(current);
        double currentDistanceX = Math.sin(bearing2 * Math.PI / OneEightyDeg) * hypotenuse2;
        //                           "percentage to mark the position"
        double totalHypotenuse2 = upperLeft.distanceTo(lowerRight);
        double totalDistanceX = totalHypotenuse2 * Math.sin(upperLeft.bearingTo(lowerRight) * Math.PI / OneEightyDeg);
        double currentPixelX = currentDistanceX / totalDistanceX * ImageSizeW;

        return new double[]{currentPixelX, currentPixelY};

    }catch(Exception e){

        e.printStackTrace();
        return new double[]{0,0};

    }

回答1:

I might be completely wrong but I think this is a way to get the angle of rotation (or maybe the 4 corners):

  1. Get the angle alpha between lowerRight and upperLeft. I guess this is equal 180 - lowerRight.bearingTo(upperLeft) EDIT (This equation should be dependent on the quadrant of the screen)
  2. Use your X,Y (screen) coordinates to get the angle theta between the hypotenuse and the lower edge. I guess (again) the sine of this angle is equal to: (height) / ((height^2) + (width^2))^1/2
  3. Now the angle of rotation is equal to theta - alpha

If you want to visualize this:



回答2:

First, map your google map coord to your custom image as if the image is NOT at an angle. Here you are basically dealing with scaling and offsets. Assume the coord you get is (x, y).

Then, rotate your coord according to this matrix: Rotate a point around origin. Since you already know the angle, you know / can calculate that center point coord that your custom map is rotating around. Assume it's (a, b). Specifically:

1) turn your map coord (x, y) to a (0,0) based coord, (x-a, y-b),

2) rotate using that matrix and you have a new coord (x', y'),

3) turn it back to (a, b) based coord, (x'+a, y'+b).



回答3:

I'm not sure if I understood your question, but can't you get the (X, Y) location from the API like this?

// myMap is a GoogleMap instance and location is a Location instance
Projection projection = myMap.getProjection();
LatLng latlng = new LatLng(location.getLatitude(), location.getLongitude());
Point pointInTheScreen = projection.toScreenLocation(latlng);

See the GoogleMap API for further info.



回答4:

Yes you can draw points that aren't on the visible map. Get around this by setting up a LatLngBounds. Then display the map for the LatLngBounds. You can also move the map to display the LatLngBounds. There's nothing tricky here no tangents or slopes or hyperbolic curvature of the earth. All that's taken care of by the point which is in degrees so don't over think your solution. If you turn the map the points will turn with it. So add your points to the map find the bounds and tilt the camera. Tilt the camera back and all the points will still be on screen! This will get you started with the all the points on the screen.

Same as the samples get a reference to a google map.

private void setUpMapIfNeeded() {
    // Do a null check to confirm that we have not already instantiated the
    // map.

    if (mMap == null) {
        // Try to obtain the map from the SupportMapFragment.
        // mMap = mMapFragment.getMap();
        // // Check if we were successful in obtaining the map.

        // Try to obtain the map from the SupportMapFragment.
        mMap = ((SupportMapFragment) getSupportFragmentManager()
                .findFragmentById(R.id.map)).getMap();
        // use the settings maptype

        // Check if we were successful in obtaining the map.
        if (mMap != null) {
            setUpMap();
        }
    }
}

Let's get started finding the boundry of the map.

import com.google.android.gms.maps.model.LatLng;
import com.google.android.gms.maps.model.LatLngBounds;

A field for LatLngBounds and a LatLngBounds.Builder

private LatLngBounds bounds;
private LatLngBounds.Builder myLatLngBuilder;

initialize the LatLngBounds.Builder

myLatLngBuilder = new LatLngBounds.Builder();

for each point on your map.

LatLng myLatLng = new LatLng(latitude, longitude);
myLatLngBuilder.include(myLatLng);

Then when your finished adding points build your boundary.

bounds = myLatLngBuilder.build();

Now I have the bounds of all the points on my map and I can display just that area of the map. This code I lifted from the map samples.

final View mapView = getSupportFragmentManager().findFragmentById(
            R.id.map).getView();
    if (mapView.getViewTreeObserver().isAlive()) {
        mapView.getViewTreeObserver().addOnGlobalLayoutListener(
                new OnGlobalLayoutListener() {
                    @SuppressWarnings("deprecation")
                    // We use the new method when supported
                    @SuppressLint("NewApi")
                    // We check which build version we are using.
                    @Override
                    public void onGlobalLayout() {
                        if (Build.VERSION.SDK_INT < Build.VERSION_CODES.JELLY_BEAN) {
                            mapView.getViewTreeObserver()
                                    .removeGlobalOnLayoutListener(this);
                        } else {
                            mapView.getViewTreeObserver()
                                    .removeOnGlobalLayoutListener(this);
                        }
                        mMap.moveCamera(CameraUpdateFactory
                                .newLatLngBounds(bounds, 50));
                    }
                });
    }

This is essentially how my app Bestrides KML Reader displays maps so no points are off screen.

Good Luck Danny117



回答5:

You could use a Magnetic Field sensor (aka Compass) to get the device's orientation and with that, 'grid-align' your custom view to your map.