How to update marker using live location along a polyline?

 ̄綄美尐妖づ 提交于 2019-12-25 18:32:57

问题


My question titles seems to be an existing one, but here is my complete scenario.

I have an activity for Map based operations, where am drawing a polyline along a road, lets say a route between two locations. Basically the app tracks the users current location (Traveling by car). So till part everything is working, as in, the route is properly shown, device Location API is giving location updates (kindof exact), and also i was able to change the location updates smoothly,

So the issue is, the locations updates are sometimes zig zag, it might not touch the road sometimes, the location updates will be going all over the place.

I have looked into ROAD api also, but am not getting the correct help, even from some previously asked questions.

Will it be possible to make the marker move only along the road?

Any kind of help will be appreciated.


回答1:


You can snap marker to the path by projection of marker on nearest path segment. Nearest segment you can find via PolyUtil.isLocationOnPath():

PolyUtil.isLocationOnPath(carPos, segment, true, 30)

and projections of marker to that segment you can find via converting geodesic spherical coordinates into orthogonal screen coordinates calculating projection orthogonal coordinates and converting it back to spherical (WGS84 LatLng -> Screen x,y -> WGS84 LatLng):

Point carPosOnScreen = projection.toScreenLocation(carPos);
Point p1 = projection.toScreenLocation(segment.get(0));
Point p2 = projection.toScreenLocation(segment.get(1));
Point carPosOnSegment = new Point();

float denominator = (p2.x - p1.x) * (p2.x - p1.x) + (p2.y - p1.y) * (p2.y - p1.y);
// p1 and p2 are the same
if (Math.abs(denominator) <= 1E-10) {
    markerProjection = segment.get(0);
} else {
    float t = (carPosOnScreen.x * (p2.x - p1.x) - (p2.x - p1.x) * p1.x
            + carPosOnScreen.y * (p2.y - p1.y) - (p2.y - p1.y) * p1.y) / denominator;
    carPosOnSegment.x = (int) (p1.x + (p2.x - p1.x) * t);
    carPosOnSegment.y = (int) (p1.y + (p2.y - p1.y) * t);
    markerProjection = projection.fromScreenLocation(carPosOnSegment);
}

With full source code:

public class MainActivity extends AppCompatActivity implements OnMapReadyCallback {

    private GoogleMap mGoogleMap;
    private MapFragment mapFragment;

    private Button mButton;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);

        mapFragment = (MapFragment) getFragmentManager()
                .findFragmentById(R.id.map_fragment);
        mapFragment.getMapAsync(this);

        mButton = (Button) findViewById(R.id.button);
        mButton.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {

            }
        });
    }

    @Override
    public void onMapReady(GoogleMap googleMap) {
        mGoogleMap = googleMap;
        mGoogleMap.setOnMapLoadedCallback(new GoogleMap.OnMapLoadedCallback() {
            @Override
            public void onMapLoaded() {
                List<LatLng> sourcePoints = new ArrayList<>();
                PolylineOptions polyLineOptions;
                LatLng carPos;

                sourcePoints.add(new LatLng(-35.27801,149.12958));
                sourcePoints.add(new LatLng(-35.28032,149.12907));
                sourcePoints.add(new LatLng(-35.28099,149.12929));
                sourcePoints.add(new LatLng(-35.28144,149.12984));
                sourcePoints.add(new LatLng(-35.28194,149.13003));
                sourcePoints.add(new LatLng(-35.28282,149.12956));
                sourcePoints.add(new LatLng(-35.28302,149.12881));
                sourcePoints.add(new LatLng(-35.28473,149.12836));

                polyLineOptions = new PolylineOptions();
                polyLineOptions.addAll(sourcePoints);
                polyLineOptions.width(10);
                polyLineOptions.color(Color.BLUE);
                mGoogleMap.addPolyline(polyLineOptions);

                carPos = new LatLng(-35.281120, 149.129721);
                addMarker(carPos);
                mGoogleMap.moveCamera(CameraUpdateFactory.newLatLngZoom(sourcePoints.get(0), 15));

                for (int i = 0; i < sourcePoints.size() - 1; i++) {
                    LatLng segmentP1 = sourcePoints.get(i);
                    LatLng segmentP2 = sourcePoints.get(i+1);
                    List<LatLng> segment = new ArrayList<>(2);
                    segment.add(segmentP1);
                    segment.add(segmentP2);

                    if (PolyUtil.isLocationOnPath(carPos, segment, true, 30)) {
                        polyLineOptions = new PolylineOptions();
                        polyLineOptions.addAll(segment);
                        polyLineOptions.width(10);
                        polyLineOptions.color(Color.RED);
                        mGoogleMap.addPolyline(polyLineOptions);
                        LatLng snappedToSegment = getMarkerProjectionOnSegment(carPos, segment, mGoogleMap.getProjection());
                        addMarker(snappedToSegment);
                        break;
                    }
                }
            }
        });
        mGoogleMap.animateCamera(CameraUpdateFactory.newLatLngZoom(sourcePoints.get(0), 15));
    }

    private LatLng getMarkerProjectionOnSegment(LatLng carPos, List<LatLng> segment, Projection projection) {
        LatLng markerProjection = null;

        Point carPosOnScreen = projection.toScreenLocation(carPos);
        Point p1 = projection.toScreenLocation(segment.get(0));
        Point p2 = projection.toScreenLocation(segment.get(1));
        Point carPosOnSegment = new Point();

        float denominator = (p2.x - p1.x) * (p2.x - p1.x) + (p2.y - p1.y) * (p2.y - p1.y);
        // p1 and p2 are the same
        if (Math.abs(denominator) <= 1E-10) {
            markerProjection = segment.get(0);
        } else {
            float t = (carPosOnScreen.x * (p2.x - p1.x) - (p2.x - p1.x) * p1.x
                    + carPosOnScreen.y * (p2.y - p1.y) - (p2.y - p1.y) * p1.y) / denominator;
            carPosOnSegment.x = (int) (p1.x + (p2.x - p1.x) * t);
            carPosOnSegment.y = (int) (p1.y + (p2.y - p1.y) * t);
            markerProjection = projection.fromScreenLocation(carPosOnSegment);
        }    
        return markerProjection;
    }

    public void addMarker(LatLng latLng) {
        mGoogleMap.addMarker(new MarkerOptions()
                .position(latLng)
        );
    }
}

you'll got something like that:

But better way is to calculate car distance from start of the path and find it position on path via SphericalUtil.interpolate() because if several path segments is close one to another (e.g. on different lanes of same road) like that:

to current car position may be closest "wrong" segment. So, calculate distance of the car from the start of the route and use SphericalUtil.interpolate() for determine point exactly on path.



来源:https://stackoverflow.com/questions/54903593/how-to-update-marker-using-live-location-along-a-polyline

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!