Как обновить маркер, используя местоположение в реальном времени вдоль полилинии?

1

Названия моих вопросов кажутся уже существующими, но вот мой полный сценарий.

У меня есть действие для операций на основе карты, где я рисую ломаную вдоль дороги, скажем, маршрут между двумя точками. В основном приложение отслеживает текущее местоположение пользователя (Путешествие на машине). Таким образом, пока часть все не работает, как показано на рисунке, маршрут правильно отображается, API определения местоположения устройства предоставляет обновления местоположения (вроде точного), а также я смог плавно изменить обновления местоположения,

Так что проблема в том, что обновления локаций иногда зигзагообразные, иногда это может не коснуться дороги, обновления локаций будут происходить повсюду.

Я также посмотрел на ROAD API, но не получаю правильную помощь, даже от некоторых ранее заданных вопросов.

Можно ли будет заставить маркер двигаться только по дороге?

Любая помощь будет оценена.

Теги:
google-maps
location
gps
google-roads-api

1 ответ

1
Лучший ответ

Вы можете привязать маркер к пути, проецируя маркер на ближайший сегмент пути. Ближайший сегмент вы можете найти через PolyUtil.isLocationOnPath():

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

и проекции маркера на этот сегмент, которые вы можете найти путем преобразования геодезических сферических координат в ортогональные экранные координаты, вычисления ортогональных координат проекции и преобразования его обратно в сферический (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);
}

С полным исходным кодом:

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)
        );
    }
}

вы получите что-то подобное:

Изображение 174551

Но лучший способ - это вычислить расстояние до начала пути и найти его положение на пути с помощью SphericalUtil.interpolate() потому что, если несколько отрезков пути расположены близко друг к другу (например, на разных полосах одной и той же дороги), вот так:

Изображение 174551

К текущей позиции автомобиля может быть ближайший "неправильный" сегмент. Итак, рассчитайте расстояние до начала маршрута и используйте SphericalUtil.interpolate() для точного определения точки на пути.

  • 0
    Извините за поздний ответ, увлекся какой-то другой работой, и да, обязательно попробую это и сообщу. Спасибо :)
  • 0
    Только одно сомнение, можно ли настроить маркер, привязанный к ближайшему сегменту? Причина, по которой я спрашиваю, заключается в том, что во время старта на маршруте местоположение машины может быть не рядом с полилинией, машине может потребоваться проехать около 500 метров или 1 километр, чтобы достичь маршрута (полилиния)
Показать ещё 3 комментария

Ещё вопросы

Сообщество Overcoder
Наверх
Меню