digitransit-ui/app/component/map/non-tile-layer/StopMarker.js
2025-05-27 16:19:09 +03:00

159 lines
4.6 KiB
JavaScript

import PropTypes from 'prop-types';
import React from 'react';
import cx from 'classnames';
import { routerShape } from 'found';
import { default as L } from 'leaflet';
import { stopShape, configShape } from '../../../util/shapes';
import GenericMarker from '../GenericMarker';
import Icon from '../../Icon';
import {
getCaseRadius,
getStopRadius,
getHubRadius,
} from '../../../util/mapIconUtils';
import { addAnalyticsEvent } from '../../../util/analyticsUtils';
import { PREFIX_STOPS } from '../../../util/path';
class StopMarker extends React.Component {
static propTypes = {
stop: stopShape.isRequired,
mode: PropTypes.string.isRequired,
renderName: PropTypes.bool,
disableModeIcons: PropTypes.bool,
limitZoom: PropTypes.number,
selected: PropTypes.bool,
colorOverride: PropTypes.string,
};
static defaultProps = {
renderName: false,
disableModeIcons: false,
limitZoom: undefined,
selected: false,
colorOverride: undefined,
};
static contextTypes = {
getStore: PropTypes.func.isRequired,
config: configShape.isRequired,
router: routerShape.isRequired,
};
redirectToStopPage = () => {
if (
window.location.pathname.indexOf('bike') === -1 &&
window.location.pathname.indexOf('walk') === -1
) {
const pathPrefixMatch =
window.location.pathname.match(/^\/([a-z]{2,})\//);
const context =
pathPrefixMatch && pathPrefixMatch[1] !== this.context.config.indexPath
? pathPrefixMatch[1]
: 'index';
addAnalyticsEvent({
action: 'SelectMapPoint',
category: 'Map',
name: 'stop',
type: this.props.mode.toUpperCase(),
context,
});
}
const prefix = PREFIX_STOPS;
this.context.router.push(
`/${prefix}/${encodeURIComponent(this.props.stop.gtfsId)}`,
);
};
getModeIcon = zoom => {
const iconId = `icon-icon_${this.props.mode}`;
const icon = Icon.asString({ img: iconId, className: 'mode-icon' });
let size;
if (zoom <= this.context.config.stopsSmallMaxZoom) {
size = this.context.config.stopsIconSize.small;
} else if (this.props.selected) {
size = this.context.config.stopsIconSize.selected;
} else {
size = this.context.config.stopsIconSize.default;
}
return L.divIcon({
html: icon,
iconSize: [size, size],
className: cx('cursor-pointer', this.props.mode, {
small: size === this.context.config.stopsIconSize.small,
selected: this.props.selected,
}),
});
};
getIcon = zoom => {
const scale = this.props.stop.transfer || this.props.selected ? 1.5 : 1;
let calcZoom;
if (this.props.limitZoom) {
calcZoom = Math.min(zoom, this.props.limitZoom);
} else {
calcZoom =
this.props.stop.transfer || this.props.selected
? Math.max(zoom, 15)
: zoom || 15;
}
const radius = getCaseRadius(calcZoom) * scale;
const stopRadius = getStopRadius(calcZoom) * scale;
const hubRadius = getHubRadius(calcZoom) * scale;
const inner = (stopRadius + hubRadius) / 2;
const stroke = stopRadius - hubRadius;
// see app/util/mapIconUtils.js for the canvas version
let iconSvg = `
<svg viewBox="0 0 ${radius * 2} ${radius * 2}">
<circle class="stop" cx="${radius}" cy="${radius}" r="${inner}" stroke-width="${stroke}" color="${
this.props.colorOverride
}" />
${
inner > 7 && this.props.stop.platformCode
? `<text x="${radius}" y="${radius}" text-anchor="middle" dominant-baseline="central"
fill="#333" font-size="${1.2 * inner}px"
font-family="Gotham XNarrow A, Gotham Rounded A, Gotham Rounded B, Roboto Condensed, Roboto, Arial, sans-serif"
>${this.props.stop.platformCode}</text>`
: ''
}
</svg>
`;
if (radius === 0) {
iconSvg = '';
}
return L.divIcon({
html: iconSvg,
iconSize: [radius * 2, radius * 2],
className: `${this.props.mode} cursor-pointer`,
});
};
render() {
return (
<GenericMarker
position={{
lat: this.props.stop.lat,
lon: this.props.stop.lon,
}}
getIcon={
this.context.config.map.useModeIconsInNonTileLayer &&
!this.props.disableModeIcons
? this.getModeIcon
: this.getIcon
}
id={this.props.stop.gtfsId}
renderName={this.props.renderName}
name={this.props.stop.name}
onClick={this.redirectToStopPage}
/>
);
}
}
export default StopMarker;