Названия моих вопросов кажутся уже существующими, но вот мой полный сценарий.
У меня есть действие для операций на основе карты, где я рисую ломаную вдоль дороги, скажем, маршрут между двумя точками. В основном приложение отслеживает текущее местоположение пользователя (Путешествие на машине). Таким образом, пока часть все не работает, как показано на рисунке, маршрут правильно отображается, API определения местоположения устройства предоставляет обновления местоположения (вроде точного), а также я смог плавно изменить обновления местоположения,
Так что проблема в том, что обновления локаций иногда зигзагообразные, иногда это может не коснуться дороги, обновления локаций будут происходить повсюду.
Я также посмотрел на ROAD API, но не получаю правильную помощь, даже от некоторых ранее заданных вопросов.
Можно ли будет заставить маркер двигаться только по дороге?
Любая помощь будет оценена.
Вы можете привязать маркер к пути, проецируя маркер на ближайший сегмент пути. Ближайший сегмент вы можете найти через 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)
);
}
}
вы получите что-то подобное:
Но лучший способ - это вычислить расстояние до начала пути и найти его положение на пути с помощью SphericalUtil.interpolate()
потому что, если несколько отрезков пути расположены близко друг к другу (например, на разных полосах одной и той же дороги), вот так:
К текущей позиции автомобиля может быть ближайший "неправильный" сегмент. Итак, рассчитайте расстояние до начала маршрута и используйте SphericalUtil.interpolate()
для точного определения точки на пути.