Skip to content
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
21 changes: 21 additions & 0 deletions config.js
Original file line number Diff line number Diff line change
Expand Up @@ -45,6 +45,27 @@ const config = {
//
// E.g. the 'bike' entry will add a "bike" profile for which we send a request with the specified 'details' parameter. You can even change the profile itself when you specify
// bike: { profile: 'raw_bike', ... }

// You can 'collapse' or group certain profiles to reduce the number of profiles in the panel. Instead they're listed in the settings but still a profile icon is shown.
// Note: the name of the group must be the default option for this group.
// profile_group_mapping: {
// car: {
// options: [
// { profile: 'car' },
// { profile: 'car_avoid_motorway' },
// { profile: 'car_avoid_ferry' },
// { profile: 'car_avoid_toll' }
// ]
// },
// bike: {
// options: [
// { profile: 'bike' },
// { profile: 'mtb' },
// { profile: 'racingbike' },
// { profile: 'ecargobike' }
// ]
// }
// }
}

// this is needed for jest (with our current setup at least)
Expand Down
2 changes: 1 addition & 1 deletion src/App.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -192,7 +192,7 @@ function LargeScreenLayout({ query, route, map, error, mapOptions, encodedValues
drawAreas={drawAreas}
/>
)}
<Search points={query.queryPoints} map={map} />
<Search points={query.queryPoints} profile={query.routingProfile} map={map} />
<div>{!error.isDismissed && <ErrorMessage error={error} />}</div>
<RoutingResults
info={route.routingResult.info}
Expand Down
8 changes: 8 additions & 0 deletions src/actions/Actions.ts
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,14 @@ export class SetVehicleProfile implements Action {
}
}

export class SetVehicleProfileGroup implements Action {
readonly group: string

constructor(group: string) {
this.group = group
}
}

export class AddPoint implements Action {
readonly atIndex: number
readonly coordinate: Coordinate
Expand Down
2 changes: 1 addition & 1 deletion src/api/Api.ts
Original file line number Diff line number Diff line change
Expand Up @@ -445,7 +445,7 @@ export class ApiImpl implements Api {

public static isMotorVehicle(profile: string) {
return (
profile.includes('car') ||
(profile.includes('car') && !profile.includes('cargobike')) ||
profile.includes('truck') ||
profile.includes('scooter') ||
profile.includes('bus') ||
Expand Down
5 changes: 5 additions & 0 deletions src/custom.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,10 @@ declare module 'heightgraph/src/heightgraph'
declare module 'custom-model-editor/src/index'

declare module 'config' {
interface ProfileGroup {
readonly options: { profile: string }[]
}

const routingApi: string
const geocodingApi: string
const defaultTiles: string
Expand All @@ -31,6 +35,7 @@ declare module 'config' {
}
maxZoom?: number
}
const profile_group_mapping: Record<string, ProfileGroup>
const profiles: object
}

Expand Down
2 changes: 1 addition & 1 deletion src/map/findNextWayPoint.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import { Coordinate } from '@/stores/QueryStore'
import { calcDist } from '@/distUtils'
import { calcDist } from '@/utils'

/**
* Finds the way-point that follows the part of a route that is closest to a given location
Expand Down
2 changes: 1 addition & 1 deletion src/pathDetails/PathDetails.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ import Dispatcher from '@/stores/Dispatcher'
import { PathDetailsElevationSelected, PathDetailsHover, PathDetailsRangeSelected } from '@/actions/Actions'
import QueryStore, { Coordinate, QueryPointType } from '@/stores/QueryStore'
import { Position } from 'geojson'
import { calcDist } from '@/distUtils'
import { calcDist } from '@/utils'
import { tr } from '@/translation/Translation'
import { SettingsContext } from '@/contexts/SettingsContext'

Expand Down
2 changes: 1 addition & 1 deletion src/sidebar/MobileSidebar.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -74,7 +74,7 @@ export default function ({ query, route, error, encodedValues, drawAreas, map }:
drawAreas={drawAreas}
/>
)}
<Search points={query.queryPoints} map={map} />
<Search points={query.queryPoints} profile={query.routingProfile} map={map} />
</div>
)}
{!error.isDismissed && <ErrorMessage error={error} />}
Expand Down
2 changes: 1 addition & 1 deletion src/sidebar/RoutingResults.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ import Details from '@/sidebar/list.svg'
import GPXDownload from '@/sidebar/file_download.svg'
import Instructions from '@/sidebar/instructions/Instructions'
import { LineString, Position } from 'geojson'
import { calcDist } from '@/distUtils'
import { calcDist } from '@/utils'
import { useMediaQuery } from 'react-responsive'
import { tr } from '@/translation/Translation'
import { ApiImpl } from '@/api/Api'
Expand Down
20 changes: 19 additions & 1 deletion src/sidebar/SettingsBox.module.css
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,23 @@
align-items: center;
}

.groupProfileOptionsHeader {
font-weight: bold;
}

.groupProfileOptions div {
padding: 4px 20px;
}

.groupProfileOptions label,
input {
cursor: pointer;
}

.groupProfileOptions label {
margin-left: 10px;
}

.toggleButton {
display: flex;
align-items: center;
Expand Down Expand Up @@ -73,7 +90,8 @@

.title,
.infoLine,
.settingsTable {
.settingsTable,
.groupProfileOptionsHeader {
font-size: 14px;
color: gray;
}
39 changes: 34 additions & 5 deletions src/sidebar/SettingsBox.tsx
Original file line number Diff line number Diff line change
@@ -1,18 +1,47 @@
import { UpdateSettings } from '@/actions/Actions'
import { SetVehicleProfile, UpdateSettings } from '@/actions/Actions'
import Dispatcher from '@/stores/Dispatcher'
import styles from '@/sidebar/SettingsBox.module.css'
import { tr } from '@/translation/Translation'
import PlainButton from '@/PlainButton'
import OnIcon from '@/sidebar/toggle_on.svg'
import OffIcon from '@/sidebar/toggle_off.svg'
import { useContext, useState } from 'react'
import { useContext } from 'react'
import { SettingsContext } from '@/contexts/SettingsContext'
import { RoutingProfile } from '@/api/graphhopper'
import * as config from 'config'
import { ProfileGroupMap } from '@/utils'

export default function SettingsBox() {
export default function SettingsBox({ profile }: { profile: RoutingProfile }) {
const settings = useContext(SettingsContext)

function setProfile(n: string) {
Dispatcher.dispatch(new SetVehicleProfile({ name: profile.name === n ? 'car' : n }))
}

const groupName = ProfileGroupMap.create(config.profile_group_mapping)[profile.name]
const group = config.profile_group_mapping[groupName]
return (
<div className={styles.parent}>
{groupName && <span className={styles.groupProfileOptionsHeader}>{tr(groupName + '_settings')}</span>}
{groupName && (
<div className={styles.settingsTable}>
<div className={styles.groupProfileOptions}>
{group.options.map(option => (
<div key={option.profile}>
<input
checked={profile.name === option.profile}
type="radio"
id={option.profile}
name={groupName}
value={option.profile}
onChange={() => setProfile(option.profile)}
/>
<label htmlFor={option.profile}>{tr(groupName + '_settings_' + option.profile)}</label>
</div>
))}
</div>
</div>
)}
<div className={styles.title}>{tr('settings')}</div>
<div className={styles.settingsTable}>
<SettingsToggle
Expand Down Expand Up @@ -75,8 +104,8 @@ function SettingsToggle({ title, enabled, onClick }: { title: string; enabled: b

function SettingsCheckbox({ title, enabled, onClick }: { title: string; enabled: boolean; onClick: () => void }) {
return (
<div className={styles.settingsCheckbox} onClick={onClick}>
<input type="checkbox" checked={enabled} onChange={ignore => {}}></input>
<div className={styles.settingsCheckbox}>
<input type="checkbox" checked={enabled} onChange={onClick}></input>
<label style={{ color: enabled ? '#5b616a' : 'gray' }}>{title}</label>
</div>
)
Expand Down
14 changes: 12 additions & 2 deletions src/sidebar/search/Search.module.css
Original file line number Diff line number Diff line change
Expand Up @@ -49,12 +49,22 @@
grid-column: 1 / span 3;
}

.addSearchBox,
.settingsButton {
.addSearchBox {
font-size: 14px;
color: gray;
}

.settingsButton,
.settingsButton:hover {
font-size: 14px;
font-weight: bold;
color: rgba(39, 100, 200, 0.85);
}

.settingsButton:hover {
color: rgb(35, 92, 185);
}

.lastSearchLine {
display: flex;
flex-direction: row;
Expand Down
5 changes: 3 additions & 2 deletions src/sidebar/search/Search.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -13,8 +13,9 @@ import AddressInput from '@/sidebar/search/AddressInput'
import { MarkerComponent } from '@/map/Marker'
import { tr } from '@/translation/Translation'
import SettingsBox from '@/sidebar/SettingsBox'
import { RoutingProfile } from '@/api/graphhopper'

export default function Search({ points, map }: { points: QueryPoint[]; map: Map }) {
export default function Search({ points, profile, map }: { points: QueryPoint[]; profile: RoutingProfile; map: Map }) {
const [showSettings, setShowSettings] = useState(false)
const [showTargetIcons, setShowTargetIcons] = useState(true)
const [moveStartIndex, onMoveStartSelect] = useState(-1)
Expand Down Expand Up @@ -62,7 +63,7 @@ export default function Search({ points, map }: { points: QueryPoint[]; map: Map
{showSettings ? tr('settings_close') : tr('settings')}
</PlainButton>
</div>
{showSettings && <SettingsBox />}
{showSettings && <SettingsBox profile={profile} />}
</div>
)
}
Expand Down
53 changes: 34 additions & 19 deletions src/sidebar/search/routingProfiles/RoutingProfiles.tsx
Original file line number Diff line number Diff line change
@@ -1,13 +1,15 @@
import React, { useEffect, useState } from 'react'
import styles from './RoutingProfiles.module.css'
import Dispatcher from '@/stores/Dispatcher'
import { SetVehicleProfile } from '@/actions/Actions'
import { SetVehicleProfile, SetVehicleProfileGroup } from '@/actions/Actions'
import { RoutingProfile } from '@/api/graphhopper'
import PlainButton from '@/PlainButton'
import Chevron from './chevron.svg'
import { tr } from '@/translation/Translation'
import CustomModelBoxSVG from '@/sidebar/open_custom_model.svg'
import { icons } from '@/sidebar/search/routingProfiles/profileIcons'
import * as config from 'config'
import { ProfileGroupMap } from '@/utils'

export default function ({
routingProfiles,
Expand Down Expand Up @@ -80,6 +82,7 @@ export default function ({
if (!icons[p.name]) customProfiles[key] = [...(customProfiles[key] || []), p.name]
})

let profileToGroup = ProfileGroupMap.create(config.profile_group_mapping)
return (
<div className={styles.profilesParent}>
<PlainButton
Expand All @@ -99,26 +102,38 @@ export default function ({
<Chevron />
</PlainButton>
<ul className={styles.profiles} id="profiles_carousel_items" onScroll={onScroll}>
{routingProfiles.map(profile => {
const className =
profile.name === selectedProfile.name
{routingProfiles
.filter(
profile => !profileToGroup[profile.name] || profile.name == profileToGroup[profile.name]
)
.map(profile => {
const isProfileSelected =
profile.name === selectedProfile.name ||
profile.name == profileToGroup[selectedProfile.name]
const className = isProfileSelected
? styles.selectedProfile + ' ' + styles.profileBtn
: styles.profileBtn
return (
<li key={profile.name}>
<PlainButton
title={tr(profile.name)}
onClick={() => Dispatcher.dispatch(new SetVehicleProfile(profile))}
className={className}
>
{customModelBoxEnabled && profile.name === selectedProfile.name && (
<CustomModelBoxSVG className={styles.asIndicator} />
)}
{getIcon(profile.name, customProfiles)}
</PlainButton>
</li>
)
})}
return (
<li key={profile.name}>
<PlainButton
title={tr(profile.name)}
onClick={() =>
Dispatcher.dispatch(
profileToGroup[profile.name]
? new SetVehicleProfileGroup(profileToGroup[profile.name])
: new SetVehicleProfile(profile)
)
}
className={className}
>
{customModelBoxEnabled && profile.name === selectedProfile.name && (
<CustomModelBoxSVG className={styles.asIndicator} />
)}
{getIcon(profile.name, customProfiles)}
</PlainButton>
</li>
)
})}
</ul>
<PlainButton
className={styles.chevron + ' ' + styles.flip}
Expand Down
25 changes: 22 additions & 3 deletions src/stores/QueryStore.ts
Original file line number Diff line number Diff line change
Expand Up @@ -17,9 +17,10 @@ import {
SetPoint,
SetQueryPoints,
SetVehicleProfile,
SetVehicleProfileGroup,
} from '@/actions/Actions'
import { Bbox, RoutingArgs, RoutingProfile } from '@/api/graphhopper'
import { calcDist } from '@/distUtils'
import { calcDist, ProfileGroupMap } from '@/utils'
import config from 'config'
import { customModel2prettyString, customModelExamples } from '@/sidebar/CustomModelExamples'

Expand All @@ -34,6 +35,7 @@ export function getBBoxFromCoord(c: Coordinate, offset: number = 0.005): Bbox {

export interface QueryStoreState {
readonly profiles: RoutingProfile[]
readonly memorizedProfilePerGroup: Record<string, string>
readonly queryPoints: QueryPoint[]
readonly nextQueryPointId: number
readonly currentRequest: CurrentRequest
Expand Down Expand Up @@ -99,6 +101,7 @@ export default class QueryStore extends Store<QueryStoreState> {

return {
profiles: [],
memorizedProfilePerGroup: {},
queryPoints: [
QueryStore.getEmptyPoint(0, QueryPointType.From),
QueryStore.getEmptyPoint(1, QueryPointType.To),
Expand Down Expand Up @@ -262,12 +265,28 @@ export default class QueryStore extends Store<QueryStoreState> {
},
true
)
} else if (action instanceof SetVehicleProfileGroup) {
let prevProfile = this.state.memorizedProfilePerGroup[action.group]

// here we assume the name of the group can be used as default profile
if (!prevProfile) prevProfile = action.group

const newState: QueryStoreState = {
...state,
routingProfile: { ...this.state.routingProfile, name: prevProfile },
}
return this.routeIfReady(newState, true)
} else if (action instanceof SetVehicleProfile) {
const name = action.profile.name
const profileToGroup = ProfileGroupMap.create(config.profile_group_mapping)
const groupName = profileToGroup[name]
const newState: QueryStoreState = {
...state,
routingProfile: action.profile,
routingProfile: { ...action.profile, name: name },
// keep track of "selected option" like car_avoid_motorway for group 'car' and if we switch back to
// this group ('car') then we still want the profile car_avoid_motorway
memorizedProfilePerGroup: { ...state.memorizedProfilePerGroup, [groupName]: name },
}

return this.routeIfReady(newState, true)
} else if (action instanceof SetCustomModel) {
const newState = {
Expand Down
Loading