Skip to content

Commit

Permalink
1 iteration smooth moving with moving marker
Browse files Browse the repository at this point in the history
  • Loading branch information
hiba9201 committed Jan 15, 2023
1 parent 7cb74dc commit 01cc842
Show file tree
Hide file tree
Showing 4 changed files with 250 additions and 110 deletions.
2 changes: 2 additions & 0 deletions components/Map/Stations/MapStations.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,7 @@ export function MapStations() {
id={station.ID}
name={station.NAME}
direction={station.DIRECTION}
key={station.ID}
/>
);
})}
Expand All @@ -48,6 +49,7 @@ export function MapStations() {
id={station.ID}
name={station.NAME}
direction={station.DIRECTION}
key={station.ID}
/>
);
})}
Expand Down
205 changes: 114 additions & 91 deletions components/Map/Vehicles/Item/MapVehiclesItem.tsx
Original file line number Diff line number Diff line change
@@ -1,65 +1,62 @@
import React, { Component, createRef } from 'react';
import React, {
useRef, useEffect, useMemo, useCallback, useState,
} from 'react';
import ReactDOMServer from 'react-dom/server';
import { isEqual } from 'lodash';
import { Marker } from 'react-leaflet';
import { useMap } from 'react-leaflet';
import L from 'leaflet';
import classNames from 'classnames/bind';

import { withMap } from 'components/hocs/withMap';
import { MovingMarker } from 'components/leaflet-extensions/moving-marker';

import { MapVehicleMarker } from '../Marker/MapVehicleMarker';

import { startMoveInDirection, clearIntervals } from './MapVehiclesItem.utils';
import { EAST_COURSE_RANGE } from './MapVehiclesItem.constants';
import { MapVehiclesItemProps } from './MapVehiclesItem.types';

import styles from './MapVehiclesItem.module.css';

const cn = classNames.bind(styles);

export class MapVehiclesItemComponent extends Component<MapVehiclesItemProps> {
private icon: L.DivIcon;
const geoCoordsToEuqlid = (oPoint: [number, number], point: [number, number]) => {
const yPoint: [number, number] = [oPoint[0], point[1]];
const xPoint: [number, number] = [point[0], oPoint[1]];

private markerRef = createRef<L.Marker>();
let x = new L.LatLng(...xPoint).distanceTo(point);
let y = new L.LatLng(...yPoint).distanceTo(point);

constructor(props: MapVehiclesItemProps) {
super(props);

this.icon = this.getIcon();
}

componentDidMount(): void {
setTimeout(this.updateTranslate, 0);

const { map } = this.props;

map.addEventListener('zoomend', () => {
this.updateTranslate();
});
if (point[0] < oPoint[0]) {
x = -x;
}

componentDidUpdate(prevProps: Readonly<MapVehiclesItemProps>): void {
const { course, position } = this.props;

if (prevProps.course !== course) {
const icon = this.getIcon();

this.markerRef?.current?.setIcon(icon);
}

if (!isEqual(prevProps.position, position)) {
this.updateTranslate();
}
if (point[1] < oPoint[1]) {
y = -y;
}

componentWillUnmount(): void {
clearIntervals();
}
return [x, y];
};

export function MapVehiclesItem({
position,
velocity,
course,
boardId,
routeNumber,
type,
disability,
warning,
onClick,
}: MapVehiclesItemProps) {
const map = useMap();
const vehicleRef = useRef<MovingMarker>();
const [isFirstPosition, setIsFirstPosition] = useState(true);
const [isActive, setIsActive] = useState(false);

const onClickEventHandler = useCallback(() => {
setIsActive(true);
onClick(routeNumber);
}, [routeNumber, onClick]);

getIcon() {
const {
boardId, routeNumber, course, type, disability, warning,
} = this.props;
const icon = useMemo(() => {
const isCourseEast = course > EAST_COURSE_RANGE.left || course < EAST_COURSE_RANGE.right;

return new L.DivIcon({
Expand All @@ -79,66 +76,92 @@ export class MapVehiclesItemComponent extends Component<MapVehiclesItemProps> {
/>,
),
});
}
}, [course, boardId, routeNumber, type, disability, warning]);

getScale = () => {
const { map } = this.props;
const rotateIcon = useCallback(() => {
vehicleRef.current.setIcon(icon);
}, [course, icon]);

// Get the y,x dimensions of the map
const { x, y } = map.getSize();
// eslint-disable-next-line @typescript-eslint/no-unused-vars
const isNewPositionBehind = useCallback(() => {
const prevPosition = vehicleRef.current.getLatLng();
const newPosition = new L.LatLng(...position);
const center: [number, number] = [prevPosition.lat, prevPosition.lng];
const xPosition: [number, number] = [prevPosition.lat, prevPosition.lng + 0.01];

// calculate the distance the one side of the map to the other using the haversine formula
const maxMeters = map
.containerPointToLatLng([0, y])
.distanceTo(map.containerPointToLatLng([x, y]));
const newPositionEuqlid = geoCoordsToEuqlid(center, position);
const xPositionEuqlid = geoCoordsToEuqlid(center, xPosition);

// calculate how many meters each pixel represents
return maxMeters / x;
};
const newPositionDistance = prevPosition.distanceTo(newPosition);

onClickEventHandler = () => {
const { routeNumber, onClick } = this.props;
if (newPositionDistance > 150) {
return false;
}

if (!routeNumber) {
return;
const xPositionDistance = prevPosition.distanceTo(xPosition);

const xMult = newPositionEuqlid[0] * xPositionEuqlid[0];
const yMult = newPositionEuqlid[1] * xPositionEuqlid[1];
const scalarMult = xMult + yMult;
const a = Math.acos(scalarMult / (xPositionDistance * newPositionDistance));
const angle = (a * 180) / Math.PI;

const diff = Math.abs(course - angle);

return diff >= 160 && diff <= 200;
}, [position, course]);

const startVehicleMoving = useCallback(
(isFirstMove: boolean) => {
if (isActive) {
// eslint-disable-next-line no-console
console.log('start moving');
}

vehicleRef.current.moveToWithDuration({
latlng: new L.LatLng(...position),
duration: isFirstMove ? 0 : 2000,
inertialMove: {
speed: velocity,
direction: course,
callback: course === 270 || velocity === 0 ? null : rotateIcon,
},
});
},
[position, velocity, course, rotateIcon],
);

useEffect(() => {
vehicleRef.current?.cancelMove();
const prevMap = vehicleRef.current ? vehicleRef.current.getMap() : null;

if (prevMap !== map) {
if (vehicleRef.current) {
vehicleRef.current.addTo(map);
} else {
const vehicle = new MovingMarker(position, {
icon,
}).addTo(map);

vehicle.on('click', onClickEventHandler);
vehicleRef.current = vehicle;
}
}

onClick(routeNumber);
};
startVehicleMoving(isFirstPosition);

private updateTranslate = () => {
const {
boardId, routeNumber, velocity, course,
} = this.props;
if (isFirstPosition) {
setIsFirstPosition(false);
}

const marker = document.querySelector(
`#vehicle-${boardId}-${routeNumber}`,
) as HTMLDivElement;
return () => {
const prevMapCheck = vehicleRef.current ? vehicleRef.current.getMap() : null;

if (!marker) {
return;
}
if (prevMapCheck !== map) {
vehicleRef.current.remove();
}
};
}, [map, position, velocity, course]);

startMoveInDirection({
direction: course,
vehicle: marker,
velocity,
scale: this.getScale(),
});
};

render() {
const { position } = this.props;

return (
<Marker
icon={this.icon}
position={position}
eventHandlers={{ click: this.onClickEventHandler }}
ref={this.markerRef}
/>
);
}
return null;
}

export const MapVehiclesItem = withMap(MapVehiclesItemComponent);
3 changes: 1 addition & 2 deletions components/Map/Vehicles/Item/MapVehiclesItem.types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,13 +4,12 @@ export type MapVehiclesItemProps = {
boardId: number;
velocity: number;
position: [number, number];
routeNumber: number | null;
routeNumber: number;
course: number;
type: VehicleType;
disability?: boolean;
warning?: boolean;
onClick: (routeNumber: number) => void;
map: L.Map;
};

export type MoveInDirectionParams = {
Expand Down
Loading

1 comment on commit 01cc842

@ekbdev
Copy link

@ekbdev ekbdev commented on 01cc842 Jan 15, 2023

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Deploy preview for ekbdev-transport ready!

✅ Preview
https://ekbdev-transport-4iofw4dyn-ekbdev.vercel.app
https://ekbdev-transport-hiba9201-moving-of-vehicles-on-moving-marker.vercel.app

Built with commit 01cc842.
This pull request is being automatically deployed with vercel-action

Please sign in to comment.