Вопрос или проблема
Когда маршрут отображается для одного режима транспорта, и я выбираю другой режим транспорта, старый маршрут остается, и в итоге я вижу 2 маршрута.
import './App.css';
import React, { useState, useEffect } from 'react';
import SearchBar from './components/SearchBar';
import {
APIProvider,
Map,
useMapsLibrary,
useMap,
} from "@vis.gl/react-google-maps";
const App = () => {
const [mapVisible, setMapVisible] = useState(false);
const [destinationPlaceID, setDestinationPlaceID] = useState(null);
const [originPlaceID, setOriginPlaceID] = useState(null);
const [modeOfTravel, setModeOfTravel] = useState('DRIVING');
const position = { lat: 1.3397443, lng: 103.7067297 };
// Функция для начала маршрутизации с выбранным местом назначения
const startRouting = (placeID) => {
setDestinationPlaceID(placeID);
setMapVisible(true); // Показываем карту, когда начинается маршрутизация
};
// Функция для получения Place ID по координатам пользователя (Возвращает Promise)
const getPlaceIdFromCoordinates = (latitude, longitude) => {
return new Promise((resolve, reject) => {
const geocoder = new google.maps.Geocoder();
geocoder.geocode(
{ location: { lat: latitude, lng: longitude } },
(results, status) => {
if (status === google.maps.GeocoderStatus.OK) {
if (results[0]) {
const placeId = results[0].place_id;
resolve(placeId); // Разрешаем с Place ID
} else {
reject('Результаты не найдены');
}
} else {
reject('Ошибка геокодирования: ' + status);
}
}
);
});
};
function Directions() {
const map = useMap();
const routesLibrary = useMapsLibrary('routes');
const [directionsService, setDirectionsService] = useState(null);
const [directionsRenderer, setDirectionsRenderer] = useState(null);
const [routes, setRoutes] = useState([]);
const [routeIndex, setRouteIndex] = useState(0);
const selected = routes[routeIndex];
const leg = selected?.legs[0];
useEffect(() => {
console.log("Карта загружена с увеличением:", map.getZoom()); // Это будет выводить начальный уровень увеличения
}, [map]);
useEffect(() => {
if (!routesLibrary || !map) return;
setDirectionsService(new routesLibrary.DirectionsService());
setDirectionsRenderer(new routesLibrary.DirectionsRenderer({ map }));
}, [routesLibrary, map]);
// Получаем направления, как только установлен originPlaceID
useEffect(() => {
if (!directionsService || !directionsRenderer || !originPlaceID) return;
(async () => {
try {
const response = await directionsService.route({
origin: { placeId: originPlaceID },
destination: { placeId: destinationPlaceID },
travelMode: google.maps.TravelMode[modeOfTravel],
provideRouteAlternatives: true,
});
directionsRenderer.setDirections(response);
setRoutes(response.routes);
setRouteIndex(0);
} catch (error) {
console.error('Ошибка при получении направлений:', error);
}
})();
}, [directionsService, directionsRenderer, destinationPlaceID, originPlaceID, modeOfTravel]);
useEffect(() => {
if (!directionsRenderer) return;
directionsRenderer.setRouteIndex(routeIndex);
}, [routeIndex, directionsRenderer]);
if (!leg) return null;
return (
<div className="directions">
<h2 className="mapTitle">{selected.summary}</h2>
<p>{leg.start_address.split(',')[0]} до {leg.end_address.split(',')[0]}</p>
<p>Расстояние: {leg.distance?.text}</p>
<p>Продолжительность: {leg.duration?.text}</p>
<br></br>
<h2 className="mapTitle">Другие маршруты</h2>
<ul class="otherRoute">
{routes.map((route, index) => (
<li key={route.summary}>
<button id="listText" onClick={() => setRouteIndex(index)}>{route.summary}</button>
</li>
))}
</ul>
</div>
);
}
// Эффект геолокации для получения origin Place ID
useEffect(() => {
if (navigator.geolocation) {
navigator.geolocation.getCurrentPosition(async (position) => {
const { latitude, longitude } = position.coords;
try {
const placeId = await getPlaceIdFromCoordinates(latitude, longitude);
setOriginPlaceID(placeId); // Устанавливаем origin Place ID
} catch (error) {
console.error('Ошибка геокодирования:', error);
}
}, () => {
alert('Геолокация не поддерживается этим браузером.');
});
} else {
alert('Геолокация не поддерживается этим браузером.');
}
}, []);
return (
<div>
<h1>Найти и маршрутизировать</h1>
<SearchBar startRouting={startRouting} />
<label htmlFor="modeOfTravel">Выберите режим передвижения:</label>
<select
id="modeOfTravel"
value={modeOfTravel}
onChange={(e) => setModeOfTravel(e.target.value)}
>
<option value="DRIVING">На машине</option>
<option value="WALKING">Пешком</option>
<option value="BICYCLING">На велосипеде</option>
<option value="TRANSIT">Общественный транспорт</option>
</select>
{mapVisible && (
<div style={{ height: "100vh", width: "100%" }} id="map">
<APIProvider apiKey={import.meta.env.VITE_GOOGLE_API_KEY}>
<Map
defaultCenter={position}
defaultZoom={15}
options={{
zoomControl: true,
gestureHandling: "auto",
scrollwheel: true,
mapTypeControl:true,
scaleControl:false,
streetViewControl:false,
overviewMapControl:false,
rotateControl:false,
}}
>
{destinationPlaceID && <Directions />}
</Map>
</APIProvider>
</div>
)}
</div>
);
};
export default App;
Я видел множество решений, предлагающих инициализировать directionsRenderer как глобальную переменную и использовать setMap(null), однако я не могу найти успешный способ сделать это в своем коде без появления множества ошибок.
Я также пробовал установить массив маршрутов в пустой массив.
Маршруты для других режимов транспорта успешно запрашиваются и корректно отображаются, но я просто не могу избавиться от старых маршрутов.
Ответ или решение
Чтобы избавиться от старых маршрутов, отображаемых на карте при смене режима передвижения, вы можете использовать метод setMap(null)
для объекта DirectionsRenderer
. Это позволит удалить существующий маршрут, прежде чем вы отобразите новый. Давайте рассмотрим, как можно эффективно внести эти изменения в ваш код.
Корректировка кода для удаления старых маршрутов
Вам нужно будет внести изменения в ваш компонент Directions
в следующий способ:
- При каждом изменении режима передвижения или при запросе новых направлений, убедитесь, что предыдущий маршрут удалён.
- После того как вы получили новый маршрут, установите его в
DirectionsRenderer
.
Вот как можно это реализовать в вашем коде:
function Directions() {
const map = useMap();
const routesLibrary = useMapsLibrary('routes');
const [directionsService, setDirectionsService] = useState(null);
const [directionsRenderer, setDirectionsRenderer] = useState(null);
const [routes, setRoutes] = useState([]);
const [routeIndex, setRouteIndex] = useState(0);
const selected = routes[routeIndex];
const leg = selected?.legs[0];
useEffect(() => {
if (!routesLibrary || !map) return;
const service = new routesLibrary.DirectionsService();
const renderer = new routesLibrary.DirectionsRenderer({ map });
setDirectionsService(service);
setDirectionsRenderer(renderer);
// Возвращаем функцию "очистки" маршрута
return () => {
renderer.setMap(null);
};
}, [routesLibrary, map]);
// Fetch directions once `originPlaceID` is set
useEffect(() => {
if (!directionsService || !directionsRenderer || !originPlaceID) return;
(async () => {
try {
const response = await directionsService.route({
origin: { placeId: originPlaceID },
destination: { placeId: destinationPlaceID },
travelMode: google.maps.TravelMode[modeOfTravel],
provideRouteAlternatives: true,
});
// Удаляем старый маршрут
directionsRenderer.setMap(null);
// Устанавливаем новый маршрут
directionsRenderer.setDirections(response);
setRoutes(response.routes);
setRouteIndex(0);
} catch (error) {
console.error('Error fetching directions:', error);
}
})();
}, [directionsService, directionsRenderer, destinationPlaceID, originPlaceID, modeOfTravel]);
useEffect(() => {
if (!directionsRenderer) return;
directionsRenderer.setRouteIndex(routeIndex);
}, [routeIndex, directionsRenderer]);
if (!leg) return null;
return (
<div className="directions">
<h2 className="mapTitle">{selected.summary}</h2>
<p>{leg.start_address.split(',')[0]} to {leg.end_address.split(',')[0]}</p>
<p>Distance: {leg.distance?.text}</p>
<p>Duration: {leg.duration?.text}</p>
<br />
<h2 className="mapTitle">Other Routes</h2>
<ul className="otherRoute">
{routes.map((route, index) => (
<li key={route.summary}>
<button id="listText" onClick={() => setRouteIndex(index)}>{route.summary}</button>
</li>
))}
</ul>
</div>
);
}
Объяснение изменений
-
Удаление маршрута: В
useEffect
, который отвечает за инициализациюDirectionsRenderer
и связанных с ним сервисов, добавили функцию для очистки (return () => { directionsRenderer.setMap(null); }
). Она будет выполнена при размонтировании компонентаDirections
или при его переинициализации. -
Сбрасывание маршрута перед отображением нового: Перед тем как установить новый маршрут с помощью
directionsRenderer.setDirections(response)
, вызываетсяdirectionsRenderer.setMap(null);
для удаления старого маршрута.
Эти изменения должны помочь вам избавиться от старых маршрутов на карте при смене режима передвижения. Убедитесь, что вы тщательно тестируете приложение после внесения изменений, чтобы гарантировать, что все работает корректно.