Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

DT-5452 Show closed stops on map using badge icons #4970

Open
wants to merge 12 commits into
base: v3
Choose a base branch
from
23 changes: 16 additions & 7 deletions app/component/map/tile-layer/Stops.js
Original file line number Diff line number Diff line change
Expand Up @@ -52,11 +52,11 @@ class Stops {
this.tile.hilightedStops.includes(feature.properties.gtfsId);
let hasTrunkRoute = false;
let hasLocalTramRoute = false;
const routes = JSON.parse(feature.properties.routes);
if (
feature.properties.type === 'BUS' &&
this.config.useExtendedRouteTypes
) {
const routes = JSON.parse(feature.properties.routes);
if (routes.some(p => p.gtfsType === ExtendedRouteTypes.BusExpress)) {
hasTrunkRoute = true;
}
Expand All @@ -65,7 +65,6 @@ class Stops {
feature.properties.type === 'TRAM' &&
this.config.useExtendedRouteTypes
) {
const routes = JSON.parse(feature.properties.routes);
if (routes.some(p => p.gtfsType === ExtendedRouteTypes.SpeedTram)) {
hasLocalTramRoute = true;
}
Expand Down Expand Up @@ -94,6 +93,9 @@ class Stops {
mode = 'speedtram';
}

const stopOutOfService = !!feature.properties.closedByServiceAlert;
const noRoutesForStop = !routes.length;

drawStopIcon(
this.tile,
feature.geom,
Expand All @@ -107,6 +109,8 @@ class Stops {
!isNull(feature.properties.code)
),
this.config.colors.iconColors,
stopOutOfService,
noRoutesForStop,
);
}
}
Expand All @@ -123,8 +127,13 @@ class Stops {
}

getPromise(lang) {
const zoom = this.tile.coords.z + (this.tile.props.zoomOffset || 0);
const stopsUrl =
zoom >= this.config.stopsMinZoom
? this.config.URL.REALTIME_STOP_MAP
: this.config.URL.STOP_MAP;
return fetchWithLanguageAndSubscription(
`${getLayerBaseUrl(this.config.URL.STOP_MAP, lang)}${
`${getLayerBaseUrl(stopsUrl, lang)}${
this.tile.coords.z + (this.tile.props.zoomOffset || 0)
}/${this.tile.coords.x}/${this.tile.coords.y}.pbf`,
this.config,
Expand All @@ -145,19 +154,19 @@ class Stops {
this.tile.hilightedStops.length &&
this.tile.hilightedStops[0]
);
const stopLayer = vt.layers.stops || vt.layers.realtimeStops;

if (
vt.layers.stops != null &&
stopLayer != null &&
(this.tile.coords.z >= this.config.stopsMinZoom ||
hasHilightedStops)
) {
const featureByCode = {};
const hybridGtfsIdByCode = {};
const zoom = this.tile.coords.z + (this.tile.props.zoomOffset || 0);
const drawPlatforms = this.config.terminalStopsMaxZoom - 1 <= zoom;
const drawRailPlatforms = this.config.railPlatformsMinZoom <= zoom;
for (let i = 0, ref = vt.layers.stops.length - 1; i <= ref; i++) {
const feature = vt.layers.stops.feature(i);
for (let i = 0, ref = stopLayer.length - 1; i <= ref; i++) {
const feature = stopLayer.feature(i);
if (
isFeatureLayerEnabled(feature, 'stop', this.mapLayers) &&
feature.properties.type &&
Expand Down
2 changes: 1 addition & 1 deletion app/component/map/tile-layer/TileContainer.js
Original file line number Diff line number Diff line change
Expand Up @@ -244,7 +244,7 @@ class TileContainer {
}
if (
index < features.length - 1 &&
features[index + 1].feature.properties.code ===
features[index + 1]?.feature.properties.code ===
feature.feature.properties.code
) {
isCombo = true;
Expand Down
4 changes: 4 additions & 0 deletions app/configurations/config.waltti.js
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,10 @@ export default {
default: `${POI_MAP_PREFIX}/fi/stops,stations/`,
sv: `${POI_MAP_PREFIX}/sv/stops,stations/`,
},
REALTIME_STOP_MAP: {
default: `${POI_MAP_PREFIX}fi/realtimeStops,stations/`,
sv: `${POI_MAP_PREFIX}/sv/realtimeStops,stations/`,
},
RENTAL_STATION_MAP: {
default: `${POI_MAP_PREFIX}/fi/rentalStations/`,
},
Expand Down
48 changes: 48 additions & 0 deletions app/util/mapIconUtils.js
Original file line number Diff line number Diff line change
Expand Up @@ -275,6 +275,19 @@ function drawIconImageBadge(
);
}

function drawTopRightCornerIconBadge(
image,
tile,
iconTopLeftCornerX,
iconTopLeftCornerY,
width,
badgeSize,
) {
const badgeX = iconTopLeftCornerX + width / 2; // badge left corner placed at the horizontal center of the icon
const badgeY = iconTopLeftCornerY - badgeSize / 3; // badge left corner placed a third above the icon
tile.ctx.drawImage(image, badgeX, badgeY);
}

function getSelectedIconCircleOffset(zoom, ratio) {
if (zoom > 15) {
return 94 / ratio;
Expand Down Expand Up @@ -350,6 +363,38 @@ function getSmallStopIcon(type, radius, color) {
});
}

/**
* Draw a badge icon on top of the icon.
*/
function drawStopStatusBadge(
tile,
x,
y,
iconWidth,
stopOutOfService,
noRoutesForStop,
) {
const badgeSize = iconWidth * 0.75; // badge size is 75% of the icon size
const badgeImageId = stopOutOfService
? `icon-icon_stop-temporarily-closed-badge`
: `icon-icon_stop-closed-badge`;
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Which mode does this stop icon represent (the sprite files does not show up in git)?
Another option to handle badges I'm currently exploring with rental stations, is having badges included in the original svg, and activate/color them via css vars. This would decouple the code from knowing where to place the badge.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

These are universal badges for any mode. Grey clock symbol / cross on red background.


if (noRoutesForStop || stopOutOfService) {
getImageFromSpriteCache(badgeImageId, badgeSize, badgeSize).then(
badgeImage => {
drawTopRightCornerIconBadge(
badgeImage,
tile,
x,
y,
iconWidth,
badgeSize,
);
},
);
}
}

const getMemoizedStopIcon = memoize(
getSmallStopIcon,
(type, radius, color, isHilighted) =>
Expand All @@ -369,6 +414,8 @@ export function drawStopIcon(
isHilighted,
isFerryTerminal,
modeIconColors,
stopOutOfService,
noRoutesForStop,
) {
if (type === 'SUBWAY') {
return;
Expand Down Expand Up @@ -417,6 +464,7 @@ export function drawStopIcon(
color,
).then(image => {
tile.ctx.drawImage(image, x, y);
drawStopStatusBadge(tile, x, y, width, stopOutOfService, noRoutesForStop);
if (drawNumber && platformNumber) {
x += radius;
y += radius;
Expand Down
8 changes: 8 additions & 0 deletions static/assets/svg-sprite.default.svg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
8 changes: 8 additions & 0 deletions static/assets/svg-sprite.hsl.svg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
17 changes: 17 additions & 0 deletions test/unit/component/map/tile-layer/Stops.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,9 @@ describe('Stops', () => {
const config = {
URL: {
STOP_MAP: { default: 'https://localhost/stopmap/' },
REALTIME_STOP_MAP: { default: 'https://localhost/realtimestopmap/' },
},
stopsMinZoom: 13,
};

const tile = {
Expand Down Expand Up @@ -37,5 +39,20 @@ describe('Stops', () => {
new Stops({ ...tile, props: { zoomOffset: 1 } }, config, []).getPromise(); // eslint-disable-line no-new
expect(mock.called()).to.equal(true);
});

it('should make a get to realtime stops uri when zoomed in', () => {
const mock = fetchMock.get(
`${config.URL.REALTIME_STOP_MAP.default}13/1/2.pbf`,
{
status: 404,
},
);
new Stops(
{ ...tile, props: { zoomOffset: 10 } },
config,
[],
).getPromise();
expect(mock.called()).to.equal(true);
});
});
});
Loading