Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Fix Distance label position on map #56899

Merged
merged 3 commits into from
Feb 17, 2025
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
19 changes: 9 additions & 10 deletions src/components/MapView/MapView.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -228,18 +228,17 @@ const MapView = forwardRef<MapViewHandle, MapViewProps>(
const initBounds = useMemo(() => (interactive ? undefined : waypointsBounds), [interactive, waypointsBounds]);

const distanceSymbolCoorinate = useMemo(() => {
const length = directionCoordinates?.length;
// If the array is empty, return undefined
if (!length) {
return undefined;
if (!directionCoordinates?.length || !waypoints?.length) {
return;
}
const {northEast, southWest} = utils.getBounds(
waypoints.map((waypoint) => waypoint.coordinate),
directionCoordinates,
);
const boundsCenter = utils.getBoundsCenter({northEast, southWest});

// Find the index of the middle element
const middleIndex = Math.floor(length / 2);

// Return the middle element
return directionCoordinates.at(middleIndex);
}, [directionCoordinates]);
return utils.findClosestCoordinateOnLineFromCenter(boundsCenter, directionCoordinates);
}, [waypoints, directionCoordinates]);

return !isOffline && isAccessTokenSet && !!defaultSettings ? (
<View style={[style, !interactive ? styles.pointerEventsNone : {}]}>
Expand Down
22 changes: 10 additions & 12 deletions src/components/MapView/MapViewImpl.website.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -250,18 +250,17 @@ const MapViewImpl = forwardRef<MapViewHandle, MapViewProps>(
}, [waypoints, directionCoordinates, interactive, currentPosition, initialState.zoom]);

const distanceSymbolCoorinate = useMemo(() => {
const length = directionCoordinates?.length;
// If the array is empty, return undefined
if (!length) {
return undefined;
if (!directionCoordinates?.length || !waypoints?.length) {
return;
}
const {northEast, southWest} = utils.getBounds(
waypoints.map((waypoint) => waypoint.coordinate),
directionCoordinates,
);
const boundsCenter = utils.getBoundsCenter({northEast, southWest});

// Find the index of the middle element
const middleIndex = Math.floor(length / 2);

// Return the middle element
return directionCoordinates.at(middleIndex);
}, [directionCoordinates]);
return utils.findClosestCoordinateOnLineFromCenter(boundsCenter, directionCoordinates);
}, [waypoints, directionCoordinates]);

return !isOffline && !!accessToken && !!initialViewState ? (
<View
Expand Down Expand Up @@ -290,15 +289,14 @@ const MapViewImpl = forwardRef<MapViewHandle, MapViewProps>(
)}
{!!distanceSymbolCoorinate && !!distanceInMeters && !!distanceUnit && (
<Marker
key="distance"
key="distance-label"
longitude={distanceSymbolCoorinate.at(0) ?? 0}
latitude={distanceSymbolCoorinate.at(1) ?? 0}
>
<PressableWithoutFeedback
accessibilityLabel={CONST.ROLE.BUTTON}
role={CONST.ROLE.BUTTON}
onPress={toggleDistanceUnit}
style={{marginRight: 100}}
>
<View style={styles.distanceLabelWrapper}>
<View style={styles.distanceLabelText}> {DistanceRequestUtils.getDistanceForDisplayLabel(distanceInMeters, distanceUnit)}</View>
Expand Down
16 changes: 9 additions & 7 deletions src/components/MapView/MapViewTypes.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,8 @@ import type {ReactNode} from 'react';
import type {StyleProp, ViewStyle} from 'react-native';
import type {Unit} from '@src/types/onyx/Policy';

type Coordinate = [number, number];

type MapViewProps = {
// Public access token to be used to fetch map data from Mapbox.
accessToken: string;
Expand All @@ -18,7 +20,7 @@ type MapViewProps = {
// Locations on which to put markers
waypoints?: WayPoint[];
// List of coordinates which together forms a direction.
directionCoordinates?: Array<[number, number]>;
directionCoordinates?: Coordinate[];
// Callback to call when the map is idle / ready.
onMapReady?: () => void;
// Whether the map is interactable or not
Expand All @@ -33,7 +35,7 @@ type MapViewProps = {

type DirectionProps = {
// Coordinates of points that constitute the direction
coordinates: Array<[number, number]>;
coordinates: Coordinate[];
};

type PendingMapViewProps = {
Expand All @@ -53,14 +55,14 @@ type PendingMapViewProps = {
// Initial state of the map
type InitialState = {
// Coordinate on which to center the map
location: [number, number];
location: Coordinate;
zoom: number;
};

// Waypoint to be displayed on the map
type WayPoint = {
id: string;
coordinate: [number, number];
coordinate: Coordinate;
markerComponent: () => ReactNode;
};

Expand All @@ -73,9 +75,9 @@ type DirectionStyle = {
// Represents a handle to interact with a map view.
type MapViewHandle = {
// Fly to a location on the map
flyTo: (location: [number, number], zoomLevel: number, animationDuration?: number) => void;
flyTo: (location: Coordinate, zoomLevel: number, animationDuration?: number) => void;
// Fit the map view to a bounding box
fitBounds: (ne: [number, number], sw: [number, number], paddingConfig?: number | number[], animationDuration?: number) => void;
fitBounds: (ne: Coordinate, sw: Coordinate, paddingConfig?: number | number[], animationDuration?: number) => void;
};

export type {DirectionStyle, WayPoint, MapViewProps, DirectionProps, PendingMapViewProps, MapViewHandle};
export type {DirectionStyle, WayPoint, MapViewProps, DirectionProps, PendingMapViewProps, MapViewHandle, Coordinate};
88 changes: 85 additions & 3 deletions src/components/MapView/utils.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,7 @@
function getBounds(waypoints: Array<[number, number]>, directionCoordinates: undefined | Array<[number, number]>): {southWest: [number, number]; northEast: [number, number]} {
import type {LngLat} from 'react-map-gl';
import type {Coordinate} from './MapViewTypes';

function getBounds(waypoints: Coordinate[], directionCoordinates: undefined | Coordinate[]): {southWest: Coordinate; northEast: Coordinate} {
const lngs = waypoints.map((waypoint) => waypoint[0]);
const lats = waypoints.map((waypoint) => waypoint[1]);
if (directionCoordinates) {
Expand All @@ -15,7 +18,7 @@ function getBounds(waypoints: Array<[number, number]>, directionCoordinates: und
/**
* Calculates the distance between two points on the Earth's surface given their latitude and longitude coordinates.
*/
function haversineDistance(coordinate1: number[], coordinate2: number[]) {
function haversineDistance(coordinate1: Coordinate, coordinate2: Coordinate) {
// Radius of the Earth in meters
const R = 6371e3;
const lat1 = ((coordinate1.at(0) ?? 0) * Math.PI) / 180;
Expand All @@ -33,11 +36,90 @@ function haversineDistance(coordinate1: number[], coordinate2: number[]) {
return R * angularDistance;
}

function areSameCoordinate(coordinate1: number[], coordinate2: number[]) {
function areSameCoordinate(coordinate1: Coordinate, coordinate2: Coordinate) {
return haversineDistance(coordinate1, coordinate2) < 20;
}

function findClosestCoordinateOnLineFromCenter(center: LngLat, lineCoordinates: Coordinate[]): Coordinate | null {
if (!lineCoordinates || lineCoordinates.length < 2) {
return null;
}

let closestPointOnLine: Coordinate | null = null;
let minDistance = Infinity;

for (let i = 0; i < lineCoordinates.length - 1; i++) {
const startPoint = lineCoordinates.at(i);
const endPoint = lineCoordinates.at(i + 1);

if (!startPoint || !endPoint) {
break;
}

const closestPoint = closestPointOnSegment(center, startPoint, endPoint);

const distance = haversineDistance([center.lng, center.lat], [closestPoint.lng, closestPoint.lat]);

if (distance < minDistance) {
minDistance = distance;
closestPointOnLine = [closestPoint.lng, closestPoint.lat];
}
}

return closestPointOnLine;
}

/**
* Find the closest point on the line segment created by connecting start and endPoint
*/
function closestPointOnSegment(point: LngLat, startPoint: Coordinate, endPoint: Coordinate): LngLat {
const x0 = point.lng;
const y0 = point.lat;
const x1 = startPoint[0];
const y1 = startPoint[1];
const x2 = endPoint[0];
const y2 = endPoint[1];

const dx = x2 - x1;
const dy = y2 - y1;

if (dx === 0 && dy === 0) {
return {lng: x1, lat: y1};
}

const t = ((x0 - x1) * dx + (y0 - y1) * dy) / (dx * dx + dy * dy);

let closestX;
let closestY;
if (t < 0) {
closestX = x1;
closestY = y1;
} else if (t > 1) {
closestX = x2;
closestY = y2;
} else {
closestX = x1 + t * dx;
closestY = y1 + t * dy;
}

return {lng: closestX, lat: closestY};
}

function getBoundsCenter(bounds: {southWest: Coordinate; northEast: Coordinate}) {
const {
southWest: [south, west],
northEast: [north, east],
} = bounds;

const latitudeCenter = (north + south) / 2;
const longitudeCenter = (east + west) / 2;

return {lng: latitudeCenter, lat: longitudeCenter};
}

export default {
getBounds,
areSameCoordinate,
findClosestCoordinateOnLineFromCenter,
getBoundsCenter,
};