Skip to content

Commit

Permalink
Merge pull request #5134 from HSLdevcom/DT-6461
Browse files Browse the repository at this point in the history
DT-6461: Use geolocation to improve accuracy of navigation instructions
  • Loading branch information
vesameskanen authored Oct 25, 2024
2 parents 261d1ed + c9e09f1 commit a1b47ae
Show file tree
Hide file tree
Showing 10 changed files with 351 additions and 134 deletions.
61 changes: 48 additions & 13 deletions app/component/itinerary/NaviContainer.js
Original file line number Diff line number Diff line change
Expand Up @@ -23,12 +23,8 @@ const legQuery = graphql`
time
}
}
to {
stop {
parentStation {
name
}
}
vehicleRentalStation {
availableVehicles {
total
Expand All @@ -40,16 +36,15 @@ const legQuery = graphql`
}
`;

function NaviContainer({
itinerary,
focusToLeg,
relayEnvironment,
setNavigation,
mapRef,
}) {
function NaviContainer(
{ itinerary, focusToLeg, relayEnvironment, setNavigation, mapRef },
{ getStore },
) {
const [realTimeLegs, setRealTimeLegs] = useState(itinerary.legs);
const [time, setTime] = useState(Date.now());
const locationOK = useRef(true);
const position = getStore('PositionStore').getLocationState();

// update view after every 10 seconds
useEffect(() => {
checkPositioningPermission().then(permission => {
Expand Down Expand Up @@ -88,7 +83,17 @@ function NaviContainer({
});
const rtLegs = itinerary.legs.map(l => {
const rtLeg = l.id ? legMap[l.id] : null;
return rtLeg ? { ...l, ...rtLeg } : { ...l };
if (rtLeg) {
return {
...l,
...rtLeg,
to: {
...l.to,
vehicleRentalStation: rtLeg.to.vehicleRentalStation,
},
};
}
return { ...l };
});
setRealTimeLegs(rtLegs);
});
Expand Down Expand Up @@ -121,6 +126,7 @@ function NaviContainer({
mapRef?.state.mapTracking || locationOK.current ? null : focusToLeg
}
time={time}
position={position}
/>{' '}
<NaviBottom setNavigation={setNavigation} arrival={arrivalTime} />
</>
Expand All @@ -136,6 +142,10 @@ NaviContainer.propTypes = {
mapRef: PropTypes.object,
};

NaviContainer.contextTypes = {
getStore: PropTypes.func.isRequired,
};

NaviContainer.defaultProps = { mapRef: undefined };

const withRelay = createFragmentContainer(NaviContainer, {
Expand All @@ -150,6 +160,18 @@ const withRelay = createFragmentContainer(NaviContainer, {
interlineWithPreviousLeg
distance
duration
headsign
fareProducts {
product {
name
id
... on DefaultFareProduct {
price {
amount
}
}
}
}
start {
scheduledTime
estimated {
Expand All @@ -168,10 +190,19 @@ const withRelay = createFragmentContainer(NaviContainer, {
}
route {
shortName
color
}
from {
lat
lon
stop {
name
lat
lon
parentStation {
name
}
}
vehicleRentalStation {
name
rentalNetwork {
Expand All @@ -191,6 +222,10 @@ const withRelay = createFragmentContainer(NaviContainer, {
code
platformCode
vehicleMode
zoneId
parentStation {
name
}
}
vehicleParking {
name
Expand Down
51 changes: 21 additions & 30 deletions app/component/itinerary/NaviLeg.js
Original file line number Diff line number Diff line change
@@ -1,56 +1,47 @@
import React from 'react';
import { FormattedMessage, intlShape } from 'react-intl';
import PropTypes from 'prop-types';
import { legShape } from '../../util/shapes';
import Icon from '../Icon';
import { legDestination, isRental } from '../../util/legUtils';
import NaviDestination from './NaviDestination';
import { isRental } from '../../util/legUtils';
import NaviLegContent from './NaviLegContent';

const iconMap = {
BICYCLE: 'icon-icon_cyclist',
CAR: 'icon-icon_car-withoutBox',
SCOOTER: 'icon-icon_scooter_rider',
WALK: 'icon-icon_walk',
WAIT: 'icon-icon_navigation_wait',
};

/* eslint-disable jsx-a11y/no-static-element-interactions, jsx-a11y/click-events-have-key-events */
export default function NaviLeg({ leg, nextLeg }, { intl }) {
const iconName = iconMap[leg.mode];
let goTo = `navileg-${leg.mode.toLowerCase()}`;
export default function NaviLeg({ leg, nextLeg, legType }) {
const iconName = legType === 'wait' ? iconMap.WAIT : iconMap[leg.mode];
let instructions = `navileg-${leg.mode.toLowerCase()}`;

if (isRental(leg, nextLeg)) {
if (leg.mode === 'WALK' && nextLeg?.mode === 'SCOOTER') {
goTo = `navileg-rent-scooter`;
instructions = `navileg-rent-scooter`;
} else {
goTo = `navileg-rent-cycle`;
instructions = `navileg-rent-cycle`;
}
}
return (
<div>
<div className="navileg-goto">
<Icon img={iconName} color="black" className="navileg-mode" />
<div className="navileg-divider" />
<div className="navileg-destination">
<div className="destination-header">
<FormattedMessage id={goTo} defaultMessage="Go to" />
&nbsp;
{legDestination(intl, leg, null, nextLeg)}
</div>
<NaviDestination leg={leg} />
</div>
<div className="navileg-goto">
<Icon img={iconName} color="black" className="navileg-mode" />
<div className="navileg-divider" />
<div className="navileg-destination">
<NaviLegContent
leg={leg}
nextLeg={nextLeg}
instructions={instructions}
legType={legType}
/>
</div>
</div>
);
}

NaviLeg.propTypes = {
leg: legShape.isRequired,
nextLeg: legShape,
};

NaviLeg.defaultProps = {
nextLeg: null,
};

NaviLeg.contextTypes = {
intl: intlShape.isRequired,
nextLeg: legShape.isRequired,
legType: PropTypes.string.isRequired,
};
71 changes: 71 additions & 0 deletions app/component/itinerary/NaviLegContent.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,71 @@
import React from 'react';
import { FormattedMessage, intlShape } from 'react-intl';
import PropTypes from 'prop-types';
import { legShape } from '../../util/shapes';
import { legDestination } from '../../util/legUtils';
import NaviDestination from './NaviDestination';
import RouteNumber from '../RouteNumber';

export default function NaviLegContent(
{ leg, nextLeg, instructions, legType },
{ intl },
) {
if (legType === 'move') {
return (
<>
<div className="destination-header">
<FormattedMessage id={instructions} defaultMessage="Go to" />
&nbsp;
{legDestination(intl, leg, null, nextLeg)}
</div>
<NaviDestination leg={leg} />
</>
);
}

if (legType === 'wait') {
const { mode, headsign, route } = nextLeg;

const color = route.color ? route.color : 'currentColor';
const localizedMode = intl.formatMessage({
id: `${mode.toLowerCase()}`,
defaultMessage: `${mode}`,
});
return (
<>
<div className="destination-header">
<FormattedMessage
id="navigation-wait-mode"
values={{ mode: localizedMode }}
defaultMessage="Wait for"
/>
</div>
<div className="wait-leg">
<RouteNumber
mode={mode.toLowerCase()}
text={route?.shortName}
withBar
isTransitLeg
color={color}
/>
<div className="headsign">{headsign}</div>
</div>
</>
);
}
return null;
}

NaviLegContent.propTypes = {
leg: legShape.isRequired,
nextLeg: legShape.isRequired,
instructions: PropTypes.string.isRequired,
legType: PropTypes.string,
};

NaviLegContent.defaultProps = {
legType: 'move',
};
NaviLegContent.contextTypes = {
intl: intlShape.isRequired,
};
8 changes: 4 additions & 4 deletions app/component/itinerary/NaviStack.js
Original file line number Diff line number Diff line change
Expand Up @@ -4,10 +4,10 @@ import cx from 'classnames';
import NaviMessage from './NaviMessage';

// eslint-disable-next-line no-unused-vars
const NaviStack = ({ notifications, handleRemove, show }) => {
const NaviStack = ({ messages, handleRemove, show }) => {
return (
<div className={cx('info-stack', !show ? 'slide-out' : 'slide-in')}>
{notifications.map((notification, index) => (
{messages.map((notification, index) => (
<NaviMessage
key={notification.id}
severity={notification.severity}
Expand All @@ -22,8 +22,8 @@ const NaviStack = ({ notifications, handleRemove, show }) => {
};

NaviStack.propTypes = {
notifications: PropTypes.arrayOf(
PropTypes.shape({
// eslint-disable-next-line
messages: PropTypes.arrayOf( PropTypes.shape({
id: PropTypes.string.isRequired,
severity: PropTypes.string.isRequired,
}),
Expand Down
Loading

0 comments on commit a1b47ae

Please sign in to comment.