Skip to content

Commit

Permalink
Display steep hills on the map (#408)
Browse files Browse the repository at this point in the history
Arindam made the icons.

Co-authored-by: arindam1993 <[email protected]>
  • Loading branch information
graue and arindam1993 authored Jan 15, 2025
1 parent 248441a commit d46b4e8
Show file tree
Hide file tree
Showing 9 changed files with 191 additions and 51 deletions.
3 changes: 3 additions & 0 deletions icons/sdf/downhill.svg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added icons/sdf/downhill_sdf.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
15 changes: 15 additions & 0 deletions icons/sdf/generate-sdf.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
#!/bin/bash

# This script requires ImageMagick to be installed, and also the
# npm package image-sdf, globally.

for svg in *.svg; do
echo Generating SDF for $svg...
i=`basename $svg .svg`
convert -density 10000 -fill white -background black -resize 256x256 \
-gravity center -extent 280x280 \
$i.svg ${i}_intermediate.png && \
image-sdf ${i}_intermediate.png --spread 19 --downscale 1 --color black \
>${i}_sdf.png && \
rm ${i}_intermediate.png || echo Failed to convert $i
done
3 changes: 3 additions & 0 deletions icons/sdf/uphill.svg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added icons/sdf/uphill_sdf.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
55 changes: 49 additions & 6 deletions src/components/BikeHopperMap.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@ import type {
GeolocateResultEvent,
LngLatBoundsLike,
LngLatLike,
MapEvent,
MapLayerMouseEvent,
MapLayerTouchEvent,
MapRef,
Expand All @@ -41,6 +42,7 @@ import {
routesToGeoJSON,
EMPTY_GEOJSON,
BIKEABLE_HIGHWAYS,
STEP_ANNOTATIONS,
} from '../lib/geometry';
import lngLatToCoords from '../lib/lngLatToCoords';
import { isTouchMoveSignificant } from '../lib/touch';
Expand All @@ -53,6 +55,7 @@ import {
} from '../features/routeParams';
import { routeClicked } from '../features/routes';
import { mapMoved } from '../features/viewport';
import downloadImageData from '../lib/downloadImageData';
import useResizeObserver from '../hooks/useResizeObserver';
import {
BOTTOM_DRAWER_DEFAULT_SCROLL,
Expand All @@ -72,7 +75,10 @@ import classnames from 'classnames';

import LogInIcon from 'iconoir/icons/log-in.svg?react';
import LogOutIcon from 'iconoir/icons/log-out.svg?react';
import slopeDownhillIconUrl from '../../icons/sdf/downhill_sdf.png';
import slopeUphillIconUrl from '../../icons/sdf/uphill_sdf.png';
import useScreenDims from '../hooks/useScreenDims';
import Color from 'color';

const _isTouch = 'ontouchstart' in window;

Expand Down Expand Up @@ -323,13 +329,13 @@ const BikeHopperMap = forwardRef(function BikeHopperMapInternal(
// TODO handle errors as well
};

const handleMapLoad = () => {
const handleMapLoad = async (event: MapEvent) => {
if (props.onMapLoad) props.onMapLoad();
dispatch(mapLoaded());
const map = event.target;

// Make road labels larger
mapRef.current
?.getMap()
map
// This expression is copied over from mapbox-streets-v11 style,
// but with all the size values increated by 2
.setLayoutProperty('road-label', 'text-size', [
Expand Down Expand Up @@ -376,9 +382,14 @@ const BikeHopperMap = forwardRef(function BikeHopperMapInternal(
],
]);

mapRef.current
?.getMap()
.setPaintProperty('road-label', 'text-halo-width', 3);
map.setPaintProperty('road-label', 'text-halo-width', 3);

const [downslopeData, upslopeData] = await Promise.all([
downloadImageData(slopeDownhillIconUrl),
downloadImageData(slopeUphillIconUrl),
]);
map.addImage('downslope', downslopeData, { sdf: true });
map.addImage('upslope', upslopeData, { sdf: true });
};

const resizeRef = useResizeObserver(
Expand Down Expand Up @@ -707,6 +718,7 @@ const BikeHopperMap = forwardRef(function BikeHopperMapInternal(
<Layer beforeId="road-label" {...getTransitionStyle(activePath)} />
<Layer {...getTransitLabelStyle(activePath)} />
<Layer {...getBikeLabelStyle(activePath)} />
<Layer {...getHillStyle(activePath)} />
</Source>
{startCoords && (
<Marker
Expand Down Expand Up @@ -972,6 +984,37 @@ function getBikeLabelStyle(
};
}

function getHillStyle(
activePath: number | null,
): Omit<SymbolLayerSpecification, 'source'> {
return {
id: 'hillLayer',
type: 'symbol',
filter: ['all', pathIndexIs(activePath), ['has', 'steepness']],
layout: {
'icon-image': [
'case',
['==', ['get', 'steepness'], STEP_ANNOTATIONS.verySteepHillUp],
'upslope',
['==', ['get', 'steepness'], STEP_ANNOTATIONS.steepHillUp],
'upslope',
'downslope',
],
'icon-size': ['interpolate', ['linear'], ['zoom'], 12.5, 0.1, 16.5, 0.15],
'symbol-placement': 'line-center',
'icon-rotation-alignment': 'viewport',
},
minzoom: 12.5,
paint: {
'icon-color': Color('#ffa25b').darken(0.5).hex(),
'icon-halo-color': 'white',
'icon-halo-width': 0.1,
'icon-halo-blur': 0.1,
'icon-opacity': 0.9,
},
};
}

function getLegOutlineStyle(
layerId: string,
activePath: number | null,
Expand Down
12 changes: 8 additions & 4 deletions src/components/ItineraryBikeStep.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -348,12 +348,16 @@ export default function ItineraryBikeStep({
'font-medium text-white text-xs': true,
'bg-red-700': anno === STEP_ANNOTATIONS.mainRoad,
'bg-gray-500':
anno === STEP_ANNOTATIONS.steepHill ||
anno === STEP_ANNOTATIONS.verySteepHill,
anno === STEP_ANNOTATIONS.steepHillUp ||
anno === STEP_ANNOTATIONS.verySteepHillUp ||
anno === STEP_ANNOTATIONS.steepHillDown ||
anno === STEP_ANNOTATIONS.verySteepHillDown,
'bg-bikeinfragreen': !(
anno === STEP_ANNOTATIONS.mainRoad ||
anno === STEP_ANNOTATIONS.steepHill ||
anno === STEP_ANNOTATIONS.verySteepHill
anno === STEP_ANNOTATIONS.steepHillUp ||
anno === STEP_ANNOTATIONS.verySteepHillUp ||
anno === STEP_ANNOTATIONS.steepHillDown ||
anno === STEP_ANNOTATIONS.verySteepHillDown
),
})}
>
Expand Down
29 changes: 29 additions & 0 deletions src/lib/downloadImageData.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
export default function downloadImageData(url: string): Promise<ImageData> {
return new Promise((resolve, reject) => {
const img = document.createElement('img');

img.addEventListener('load', () => {
const canvas = document.createElement('canvas');
const ctx = canvas.getContext('2d');
if (!ctx) {
reject(new Error("can't create 2D draw context"));
return;
}
canvas.width = img.naturalWidth;
canvas.height = img.naturalHeight;
try {
ctx.drawImage(img, 0, 0, img.naturalWidth, img.naturalHeight);
resolve(ctx.getImageData(0, 0, img.naturalWidth, img.naturalHeight));
} catch (err) {
reject(err);
}
});

img.addEventListener('error', (evt) => {
URL.revokeObjectURL(url);
reject(evt);
});

img.src = url;
});
}
Loading

0 comments on commit d46b4e8

Please sign in to comment.