Turn by turn directions#73
Conversation
E.g. http://localhost:8000/from/my-saved-location/to/19%20East%20Chicago%20Avenue%2C%20Chicago%2C%20IL%2060611/
This reverts commit 44ce3a9.
View in map changes from that focused segment back to whole map
jeancochrane
left a comment
There was a problem hiding this comment.
Capturing some of our feedback from our in-person UX testing:
- Remove buffer around nodes when highlighting a route segment (keep the buffer around the ways)
- Handle navbar sizing on small screens (reduce logo text size, then ultimately remove logo text, leaving just the bike emoji)
- When clicking on a turn-by-turn route segment, if the directions modal is expanded to full screen, un-expand the modal so that the user can see the segment they've selected
- When clicking on a turn-by-turn route segment, if the directions modal is not expanded, then the operation to center the map viewport should exclude the portion of the map that is obscured by the directions modal
…ht when closing modal
|
Made the changes we discussed, with one difference: I made the header text "Mellow Bike Map" collapse to "MBM" on small viewports, which wasn't an option we'd considered, but seemed to me to preserve the site's branding a bit better than the bike emoji. If you'd rather stick to just getting rid of the site title and showing the bike emoji, lmk. |
jeancochrane
left a comment
There was a problem hiding this comment.
Looking great! Lots of small thoughts, but nothing disqualifying just yet.
| return ( | ||
| rows[0]["component"], | ||
| rows[0]["component_node_count"], | ||
| rows[0]["total_components"], | ||
| rows[0]["total_nodes"], | ||
| ) |
There was a problem hiding this comment.
[Suggestion, optional] It's probably fine since this is a management command and not module code, but situations like these where we're returning a bunch of values in a tuple often call for a dataclass.
| total_distance = sum( | ||
| feature.get("properties", {}).get("length_m", 0) for feature in features | ||
| ) |
There was a problem hiding this comment.
[Question, non-blocking] Isn't calculate_route() using distance as this property name instead of length_m? (source)
'distance': row['length_m'],| def _format_distance(meters: float) -> str: | ||
| meters_per_mile = 1609.344 | ||
| meters_per_foot = 0.3048 | ||
| miles = meters / meters_per_mile | ||
| def round_half_up(value: float) -> int: | ||
| return int(value + 0.5) | ||
|
|
||
| if miles < 0.09: | ||
| feet = round_half_up(meters / meters_per_foot) | ||
| unit = 'foot' if feet == 1 else 'feet' | ||
| return f"{feet} {unit}" | ||
|
|
||
| rounded_miles = round_half_up(miles * 10) / 10 | ||
| unit = 'mile' if rounded_miles == 1 else 'miles' | ||
| return f"{rounded_miles} {unit}" |
There was a problem hiding this comment.
[Question, non-blocking] This seems like a dupe of mbm.routing._format_distance(), albeit with slightly different logic. Do they really need to be two different functions? If so, is there a way we can distinguish their function names to make the difference clearer?
| let listItemHtml = `<li class="direction-item" data-direction-index="${index}" data-color="${color}"><span class="direction-icon-wrapper">${icon}</span><span class="direction-text">${directionText}` | ||
|
|
||
| listItemHtml += `</span></li>` | ||
|
|
||
| $directionsList.append(listItemHtml) |
There was a problem hiding this comment.
[Nitpick, optional] I'm not terribly concerned about it, but I think it's technically possible to insert malicious code into an OSM street name in order to get it templated in here via directionText. Whether or not that's a realistic attack vector is a separate question, but it's easy enough to use the JQuery text() API to sanitize the input (code not tested):
| let listItemHtml = `<li class="direction-item" data-direction-index="${index}" data-color="${color}"><span class="direction-icon-wrapper">${icon}</span><span class="direction-text">${directionText}` | |
| listItemHtml += `</span></li>` | |
| $directionsList.append(listItemHtml) | |
| const $li = $('<li>', { | |
| class: 'direction-item', | |
| 'data-direction-index': index, | |
| 'data-color': color, | |
| }) | |
| // icon is a static SVG string we control, so innerHTML is fine here | |
| const $iconWrapper = $('<span>', { class: 'direction-icon-wrapper' }).html(icon) | |
| const $textWrapper = $('<span>', { class: 'direction-text' }) | |
| $textWrapper.text(directionText) | |
| $li.append($iconWrapper).append($textWrapper) | |
| $directionsList.append($li) |
| onEachFeature: function (feature, layer) { | ||
| // Bind popup to allow tooltips on highlighted segments | ||
| layer.bindPopup( | ||
| `<strong>Name:</strong> ${feature.properties.name}<br>` + |
There was a problem hiding this comment.
[Nitpick, optional] Here we are also potentially directly rendering untrusted input from OSM, I think. Not totally sure the best way to do so but we should probably escape it if we want to be maximally defensive.
jeancochrane
left a comment
There was a problem hiding this comment.
Oh, I also forgot to mention -- there are some merge conflicts with the main branch, do you mind resolving those?
Expands on #48 and #68, and optionally incorporates #61, to implement #69.
Changes
?debug=trueto the URL adds a panel below each direction that shows the ways it's composed of.(As you can see from this screenshot, there are still issues with routing onto sidewalks, which I've opened a separate draft PR for, and with turns onto unnamed streets that could be more specifically identified, though many of the unnamed streets now at least have descriptions.)
To do