Обработка линий Mapbox GeoJson по всем мировым копиям? - PullRequest
0 голосов
/ 28 февраля 2019

У меня есть карта mapbox, на которой я рисую точки и рисую линии между точками для создания маршрутов.Настройка работает нормально, а анимация работает так, как задумано.

Однако, когда у меня есть линия, которая пересекает новую копию мира, изогнутая линия рисуется, но другая линия также рисуется по всей карте.

map

Кто-нибудь знает способ обработки рисования линий, чтобы они не переходили в новый мир?Или есть идеи о том, как лучше всего рисовать линии?

В качестве ссылки я использую пример mapbox, найденный здесь:

https://docs.mapbox.com/mapbox-gl-js/example/animate-point-along-route/

showRoute = route => {
		const steps = 500;

		const getRouteChunks = (array, size) => {
			const chunkedArr = [];

			array.forEach((arr, i) => {
				chunkedArr.push(array.slice(i, i + size));
			});

			return chunkedArr;
		};

		const routeChunks = getRouteChunks(route.destinations, 2);

		this.setState(prevState => {
			// Let's remove any existing route lines
			const mapLayers = prevState.map.style._layers;

			const midLayersOnlyToRemove = this.getMapboxLayersToRemove(mapLayers, '-midpoint');
			midLayersOnlyToRemove.forEach(activeLayer => {
				prevState.map.removeLayer(activeLayer.id);
			});

			const layersAndSourcesToRemove = this.getMapboxLayersToRemove(mapLayers, 'Route');
			layersAndSourcesToRemove.forEach(activeLayer => {
				prevState.map.removeLayer(activeLayer.id);
				prevState.map.removeSource(activeLayer.id);
			});

			const layersOnlyToRemove = this.getMapboxLayersToRemove(mapLayers, 'symbolPoint');
			layersOnlyToRemove.forEach(activeLayer => {
				prevState.map.removeLayer(activeLayer.id);
			});

			// Get a copy of all the geojson in state and then find the destinations we will need
			const { allGeoJson } = prevState;
			const destinationIds = route.destinations.map(dest => dest.id);
			const activeRouteDestinationsGeoJson = allGeoJson.features.filter(feature =>
				destinationIds.some(dId => dId === feature.properties.id)
			);

			// Setup the updated geojson object with the relvant destinations
			const activeRouteGeoJson = {
				features: activeRouteDestinationsGeoJson,
				type: 'FeatureCollection',
			};

			// Get the current markers that have been rendered in ReactDOM and remove them if they aren't needed
			const currentDomMarkers = document.querySelectorAll(`.current-mapoint-marker`);
			currentDomMarkers.forEach(marker => {
				const routeIds = marker.getAttribute('route-ids').split(',');
				const compareCatArrays = routeIds.some(c => c === route.id);
				if (!compareCatArrays) {
					marker.remove();
				}
			});

			// If the user clicks on a route that is already active, unset it and show all points again
			if (prevState.activeRouteId === route.id) {
				this.renderMarkers(allGeoJson);
				getMapboxJs().then(({ default: mapboxgl }) => {
					const bounds = new mapboxgl.LngLatBounds();
					allGeoJson.features.forEach(feature => {
						bounds.extend(feature.geometry.coordinates);
					});
					prevState.map.fitBounds(bounds, {
						padding: { top: 200, bottom: 200, left: 200, right: 200 },
						easing(t) {
							return t * (2 - t);
						},
					});
				});

				return { geojson: allGeoJson, activeRouteId: null, activeRoute: null };
			}

			routeChunks.forEach((chunk, i) => {
				let counter = 0;
				const icon = route.routeSteps[i];
				const getIcon = icon => {
					switch (icon) {
						case 'Flight': {
							return 'airport-15';
						}
						case 'Train': {
							return 'rail-15';
						}
						default:
							return 'airport-15';
					}
				};

				const pointExists = prevState.map.getSource(`symbolPoint-${chunk[0].id}`);
				const lineExists = prevState.map.getSource(`Route-line-${i}`);

				const chunkPoint = {
					type: 'FeatureCollection',
					features: [
						{
							type: 'Feature',
							properties: {},
							geometry: {
								type: 'Point',
								coordinates: chunk[0].coordinates,
							},
						},
					],
				};

				if (!pointExists) {
					prevState.map.addSource(`symbolPoint-${chunk[0].id}`, {
						type: 'geojson',
						data: chunkPoint,
					});
				}
				const chunkLength = chunk.length;
				if (chunkLength === 2) {
					const chunkRoute = {
						type: 'FeatureCollection',
						features: [
							{
								type: 'Feature',
								geometry: {
									type: 'LineString',
									coordinates: [chunk[0].coordinates, chunk[1].coordinates],
								},
							},
						],
					};

					const chunkLine = lineString([...chunkRoute.features[0].geometry.coordinates]);
					const chunkLineDistance = length(chunkLine, { units: 'miles' });

					const chunkArc = [];
					// Draw an arc between the `origin` & `destination` of the two points
					for (let j = 0; j < chunkLineDistance; j += chunkLineDistance / steps) {
						const segment = along(chunkRoute.features[0], j, { units: 'miles' });
						chunkArc.push(segment.geometry.coordinates);
					}

					chunkRoute.features[0].geometry.coordinates = chunkArc;

					if (!lineExists) {
						prevState.map.addSource(`Route-line-${i}`, {
							type: 'geojson',
							data: chunkRoute,
						});
					}

					prevState.map.addLayer({
						id: `Route-line-${i}`,
						source: `Route-line-${i}`,
						type: 'line',
						paint: {
							'line-width': 2,
							'line-color': '#007cbf',
						},
					});

					prevState.map.addLayer({
						id: `symbolPoint-${chunk[0].id}`,
						source: `symbolPoint-${chunk[0].id}`,
						type: 'symbol',
						layout: {
							'icon-image': getIcon(icon),
							'icon-rotate': ['get', 'bearing'],
							'icon-rotation-alignment': 'map',
							'icon-allow-overlap': true,
							'icon-ignore-placement': true,
						},
					});

					const animate = () => {
						// Update point geometry to a new position based on counter denoting
						// the index to access the arc.
						const nextPoint = chunkRoute.features[0].geometry.coordinates[counter + 1];
						if (chunkRoute.features[0].geometry.coordinates[counter] !== undefined) {
							chunkPoint.features[0].geometry.coordinates = chunkRoute.features[0].geometry.coordinates[counter];

							// Calculate the bearing to ensure the icon is rotated to match the route arc
							// The bearing is calculate between the current point and the next point, except
							// at the end of the arc use the previous point and the current point
							if (nextPoint) {
								chunkPoint.features[0].properties.bearing = bearing(
									point(chunkRoute.features[0].geometry.coordinates[counter >= steps ? counter - 1 : counter]),
									point(chunkRoute.features[0].geometry.coordinates[counter >= steps ? counter : counter + 1])
								);
							}

							// Update the source with this new data.
							prevState.map.getSource(`symbolPoint-${chunk[0].id}`).setData(chunkPoint);

							// Request the next frame of animation so long the end has not been reached.
							if (counter < steps) {
								requestAnimationFrame(animate);
							}
							counter += 1;
						}
					};

					animate(counter);
				}
			});

			getMapboxJs().then(({ default: mapboxgl }) => {
				const bounds = new mapboxgl.LngLatBounds();

				route.destinations.forEach(destination => {
					bounds.extend(destination.coordinates);
				});

				prevState.map.fitBounds(bounds, {
					padding: { top: 200, bottom: 200, left: 200, right: 200 },
					easing(t) {
						return t * (2 - t);
					},
				});
			});

			// Re-render the markers and then update state
			this.renderMarkers(activeRouteGeoJson);
			return { geojson: activeRouteGeoJson, activeRouteId: route.id, activeRoute: route };
		});
	};

1 Ответ

0 голосов
/ 03 апреля 2019

Мне удалось найти исправление для этого, используя Turf.js и используя пакет great-circle, который он имеет.Все, что вам нужно сделать, это получить координаты начальной и конечной точки линии, которую вы рисуете, и передать их в метод greatCircle, а затем использовать возвращаемую строку lineString, чтобы нарисовать линию с помощью Mapbox.

const start = point(chunk[0].coordinates);
const end = point(chunk[1].coordinates);
const circle = greatCircle(start, end);

prevState.map.addSource(`Route-line-${i}`, {
    type: 'geojson',
    data: circle,
});
...