Skip to content

Commit

Permalink
Merge pull request #1057 from opentripplanner/stricter-itinerary-order
Browse files Browse the repository at this point in the history
Stricter itinerary order
  • Loading branch information
binh-dam-ibigroup authored Nov 14, 2023
2 parents 204e799 + 530bf3c commit d5a2d8d
Show file tree
Hide file tree
Showing 12 changed files with 572 additions and 445 deletions.
3 changes: 1 addition & 2 deletions lib/components/narrative/default/default-itinerary.js
Original file line number Diff line number Diff line change
Expand Up @@ -20,8 +20,7 @@ import {
getAccessibilityScoreForItinerary,
itineraryHasAccessibilityScores
} from '../../../util/accessibility-routing'
import { getFare, getTotalFare } from '../../../util/state'
import { getFirstLegStartTime } from '../../../util/itinerary'
import { getFare, getFirstLegStartTime } from '../../../util/itinerary'
import { Icon, StyledIconWrapperTextAlign } from '../../util/styledIcon'
import { localizeGradationMap } from '../utils'
import FieldTripGroupSize from '../../admin/field-trip-itinerary-group-size'
Expand Down
9 changes: 5 additions & 4 deletions lib/components/narrative/line-itin/itin-summary.tsx
Original file line number Diff line number Diff line change
@@ -1,12 +1,13 @@
import { connect } from 'react-redux'
import { FareProductSelector, Itinerary, Leg } from '@opentripplanner/types'
import { FormattedMessage, FormattedNumber } from 'react-intl'
import coreUtils from '@opentripplanner/core-utils'
import React, { Component } from 'react'
import styled from 'styled-components'
import type { Itinerary, Leg } from '@opentripplanner/types'

import { AppReduxState } from '../../../util/state-types'
import { ComponentContext } from '../../../util/contexts'
import { getFare } from '../../../util/state'
import { getFare } from '../../../util/itinerary'
import FormattedDuration from '../../util/formatted-duration'

// TODO: make this a prop
Expand Down Expand Up @@ -77,7 +78,7 @@ const ShortName = styled.div<{ leg: Leg }>`

type Props = {
currency?: string
defaultFareType: string
defaultFareType?: FareProductSelector
itinerary: Itinerary
onClick: () => void
}
Expand Down Expand Up @@ -236,7 +237,7 @@ function getRouteColorForBadge(leg: Leg): string {
return leg.routeColor ? '#' + leg.routeColor : defaultRouteColor
}

const mapStateToProps = (state: any) => {
const mapStateToProps = (state: AppReduxState) => {
return {
defaultFareType: state.otp.config.itinerary?.defaultFareType
}
Expand Down
17 changes: 4 additions & 13 deletions lib/components/narrative/metro/departure-times-list.tsx
Original file line number Diff line number Diff line change
@@ -1,30 +1,21 @@
import { FormattedList, useIntl } from 'react-intl'
import { Itinerary, Leg } from '@opentripplanner/types'
import React, { MouseEvent, useCallback } from 'react'

import { firstTransitLegIsRealtime } from '../../../util/viewer'
import {
getFirstLegStartTime,
getLastLegEndTime
getLastLegEndTime,
ItineraryStartTime,
ItineraryWithIndex
} from '../../../util/itinerary'
import InvisibleA11yLabel from '../../util/invisible-a11y-label'

interface ItineraryWithIndex extends Itinerary {
index: number
}

interface StartTime {
itinerary: ItineraryWithIndex
legs: Leg[]
realtime: boolean
}

export type SetActiveItineraryHandler = (payload: { index: number }) => void

type DepartureTimesProps = {
expanded?: boolean
itinerary: ItineraryWithIndex & {
allStartTimes?: StartTime[]
allStartTimes?: ItineraryStartTime[]
}
setActiveItinerary: SetActiveItineraryHandler
showArrivals?: boolean
Expand Down
7 changes: 4 additions & 3 deletions lib/components/narrative/metro/metro-itinerary.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -12,13 +12,15 @@ import React from 'react'
import styled, { keyframes } from 'styled-components'

import * as uiActions from '../../../actions/ui'
import { AppReduxState } from '../../../util/state-types'
import { ComponentContext } from '../../../util/contexts'
import { FlexIndicator } from '../default/flex-indicator'
import {
getAccessibilityScoreForItinerary,
itineraryHasAccessibilityScores
} from '../../../util/accessibility-routing'
import { getActiveSearch, getFare } from '../../../util/state'
import { getActiveSearch } from '../../../util/state'
import { getFare } from '../../../util/itinerary'
import { IconWithText } from '../../util/styledIcon'
import { ItineraryDescription } from '../default/itinerary-description'
import { ItineraryView } from '../../../util/ui'
Expand Down Expand Up @@ -451,8 +453,7 @@ class MetroItinerary extends NarrativeItinerary {
}
}

// TODO: state type
const mapStateToProps = (state: any, ownProps: Props) => {
const mapStateToProps = (state: AppReduxState, ownProps: Props) => {
const activeSearch = getActiveSearch(state)

return {
Expand Down
94 changes: 23 additions & 71 deletions lib/components/narrative/narrative-itineraries.js
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ import { FormattedMessage, injectIntl } from 'react-intl'
import { isFlex, isTransit } from '@opentripplanner/core-utils/lib/itinerary'
import clone from 'clone'
import coreUtils from '@opentripplanner/core-utils'
import memoize from 'lodash.memoize'
import PropTypes from 'prop-types'
import React, { Component } from 'react'
import Skeleton, { SkeletonTheme } from 'react-loading-skeleton'
Expand All @@ -17,7 +18,8 @@ import {
getActiveSearch,
getRealtimeEffects,
getResponsesWithErrors,
getVisibleItineraryIndex
getVisibleItineraryIndex,
sortItinerariesInPlaceIfNeeded
} from '../../util/state'
import {
getFirstLegStartTime,
Expand Down Expand Up @@ -52,12 +54,11 @@ function makeStartTime(itinerary) {
}
}

function doMergeItineraries(itineraries) {
const doMergeItineraries = memoize((itineraries) => {
const mergedItineraries = itineraries
.reduce((prev, cur, curIndex) => {
.reduce((prev, cur) => {
const updatedItineraries = clone(prev)
const updatedItinerary = clone(cur)
updatedItinerary.index = curIndex

const duplicateIndex = updatedItineraries.findIndex((itin) =>
itinerariesAreEqual(itin, cur)
Expand Down Expand Up @@ -149,56 +150,7 @@ function doMergeItineraries(itineraries) {
allItineraries,
mergedItineraries
}
}

// Returns a car itinerary if there is one, otherwise returns false
function getCarItinerary(itineraries) {
const isCarOnly = (itin) =>
itin.legs.length === 1 && itin.legs[0].mode.startsWith('CAR')
return (
!!itineraries.filter(isCarOnly).length && itineraries.filter(isCarOnly)[0]
)
}

function computeCarbonBaseline(itineraries, co2Config) {
// Sums the sum of the leg distances for each leg
const avgDistance =
itineraries.reduce(
(sum, itin) =>
sum + itin.legs.reduce((legsum, leg) => legsum + leg.distance, 0),
0
) / itineraries.length

// If we do not have a drive yourself itinerary, estimate the distance based on avg of transit distances.
return coreUtils.itinerary.calculateEmissions(
getCarItinerary(itineraries) || {
legs: [{ distance: avgDistance, mode: 'CAR' }]
},
co2Config?.carbonIntensity,
co2Config?.massUnit
)
}

function addCarbonInfo(itin, co2Config, baselineCo2) {
const emissions = coreUtils.itinerary.calculateEmissions(
itin,
co2Config?.carbonIntensity,
co2Config?.massUnit
)
return {
...itin,
co2: emissions,
co2VsBaseline: (emissions - baselineCo2) / baselineCo2
}
}

function addCarbonInfoToAll(itineraries, co2Config) {
const baselineCo2 = computeCarbonBaseline(itineraries, co2Config)
return (
itineraries?.map((itin) => addCarbonInfo(itin, co2Config, baselineCo2)) ||
[]
)
}
})

// FIXME: move to typescript once shared types exist
class NarrativeItineraries extends Component {
Expand Down Expand Up @@ -578,12 +530,14 @@ const reduceErrorsFromResponse = (acc, cur) => {
// connect to the redux store

const mapStateToProps = (state) => {
const { config, filter } = state.otp
const { co2, errorMessages, modes } = config
const { sort } = filter

const activeSearch = getActiveSearch(state)
const activeItinerary = activeSearch && activeSearch.activeItinerary
const { co2, errorMessages, modes } = state.otp.config
const { sort } = state.otp.filter
const activeItinerary = activeSearch?.activeItinerary
const pending = activeSearch?.pending > 0
const itineraries = getActiveItineraries(state)
const itinsWithCo2 = getActiveItineraries(state)
const realtimeEffects = getRealtimeEffects(state)
const urlParams = coreUtils.query.getUrlParams()
const itineraryView = getItineraryView(urlParams)
Expand All @@ -598,36 +552,34 @@ const mapStateToProps = (state) => {
mergeItineraries,
showHeaderText,
sortModes
} = state.otp.config?.itinerary || false
} = config.itinerary || false
// Default to true for backwards compatibility
const renderSkeletons = !state.otp.config.itinerary?.hideSkeletons
const renderSkeletons = !config.itinerary?.hideSkeletons
const itineraryIsExpanded =
activeItinerary !== undefined && activeItinerary !== null && showDetails
const { localUser, loggedInUser } = state.user
const user = loggedInUser || localUser

// Add carbon info, if available.
const itinsWithCo2 = addCarbonInfoToAll(itineraries, co2)

// Merge duplicate itineraries together and save multiple departure times
let mergedItineraries
let allItineraries
if (mergeItineraries) {
// eslint-disable-next-line prettier/prettier
({ allItineraries, mergedItineraries } = doMergeItineraries(itinsWithCo2))
} else {
allItineraries = mergedItineraries = itinsWithCo2.map((itin, index) => ({
...itin,
index
}))
allItineraries = itinsWithCo2
mergedItineraries = [...allItineraries]
}

// Sort the merged (displayed) itineraries if needed
sortItinerariesInPlaceIfNeeded(mergedItineraries, state)

return {
// swap out realtime itineraries with non-realtime depending on boolean
activeItinerary,
activeLeg: activeSearch && activeSearch.activeLeg,
activeLeg: activeSearch?.activeLeg,
activeSearch,
activeStep: activeSearch && activeSearch.activeStep,
activeStep: activeSearch?.activeStep,
co2Config: co2,
customBatchUiBackground,
enabledSortModes: sortModes,
Expand All @@ -645,13 +597,13 @@ const mapStateToProps = (state) => {
mergedItineraries,
modes,
pending,
popupTarget: state.otp.config?.popups?.launchers?.optionFilter,
popupTarget: config.popups?.launchers?.optionFilter,
realtimeEffects,
renderSkeletons,
showDetails,
showHeaderText,
sort,
timeFormat: coreUtils.time.getTimeFormat(state.otp.config),
timeFormat: coreUtils.time.getTimeFormat(config),
user,
visibleItinerary: getVisibleItineraryIndex(state)
}
Expand Down
10 changes: 10 additions & 0 deletions lib/util/config-types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -226,6 +226,15 @@ export type ItinerarySortOption =
| 'COST'
| 'DEPARTURETIME'

export interface ItineraryCostWeights {
driveReluctance: number
durationFactor: number
fareFactor: number
transferReluctance: number
waitReluctance: number
walkReluctance: number
}

export interface ItineraryConfig {
costs?: ItineraryCostConfig
customBatchUiBackground?: boolean
Expand All @@ -248,6 +257,7 @@ export interface ItineraryConfig {
showPlanFirstLastButtons?: boolean
showRouteFares?: boolean
sortModes?: ItinerarySortOption[]
weights?: ItineraryCostWeights
}

export interface CO2Config extends CO2ConfigType {
Expand Down
Loading

0 comments on commit d5a2d8d

Please sign in to comment.