From a1007f5a4ffd9e6091748ddbaa02340f90b203ad Mon Sep 17 00:00:00 2001 From: thekingofcity <3353040+thekingofcity@users.noreply.github.com> Date: Wed, 19 Mar 2025 20:33:46 +0800 Subject: [PATCH 01/12] #705 Shanghai Metro 2024 style --- src/constants/constants.ts | 1 + src/svgs/shmetro/railmap-shmetro.tsx | 41 ++++++++++++++++++++++++++++ src/svgs/shmetro/runin-shmetro.tsx | 28 ++++++++++--------- src/svgs/shmetro/station-shmetro.tsx | 37 ++++++++++++++++++++++--- 4 files changed, 90 insertions(+), 17 deletions(-) diff --git a/src/constants/constants.ts b/src/constants/constants.ts index e038d7027..6ae4273ff 100644 --- a/src/constants/constants.ts +++ b/src/constants/constants.ts @@ -192,6 +192,7 @@ export enum PanelTypeGZMTR { export enum PanelTypeShmetro { sh = 'sh', sh2020 = 'sh2020', + sh2024 = 'sh2024', } /** diff --git a/src/svgs/shmetro/railmap-shmetro.tsx b/src/svgs/shmetro/railmap-shmetro.tsx index 9b3af68b0..04034fdb3 100644 --- a/src/svgs/shmetro/railmap-shmetro.tsx +++ b/src/svgs/shmetro/railmap-shmetro.tsx @@ -62,6 +62,30 @@ const DefsSHMetro = memo(function DefsSHMetro() { + + + + + + + + + + + + + + + + + + + @@ -146,6 +170,23 @@ const DefsSHMetro = memo(function DefsSHMetro() { {/* Outline filter of white pass color in Pujiang Line */} + + {/* 2024 Station border white outline */} + + + + + + + ); }); diff --git a/src/svgs/shmetro/runin-shmetro.tsx b/src/svgs/shmetro/runin-shmetro.tsx index 82b15e97c..cf607ae68 100644 --- a/src/svgs/shmetro/runin-shmetro.tsx +++ b/src/svgs/shmetro/runin-shmetro.tsx @@ -505,8 +505,8 @@ const NextText = (props: { nextName: Translation } & SVGProps) => { }; const PrevStn = (props: { stnIds: string[] }) => { - const param = useRootSelector(store => store.param); - const prevNames = props.stnIds.map(stnId => param.stn_list[stnId].localisedName); + const { stn_list, direction, svgWidth, info_panel_type } = useRootSelector(store => store.param); + const prevNames = props.stnIds.map(stnId => stn_list[stnId].localisedName); const prevHintDy = (props.stnIds.length > 1 ? 15 : 125) + prevNames.map(name => name.zh?.split('\\')?.length ?? 1).reduce((acc, cur) => acc + cur, -prevNames.length) * @@ -519,11 +519,13 @@ const PrevStn = (props: { stnIds: string[] }) => { ? (prevZhName.split('\\').length - 1) * -50 + (prevEnName.split('\\').length - 1) * -30 : 0) + 70; + const previousText = info_panel_type === 'sh2024' ? 'Previous Stop' : 'Past Stop'; + return ( {props.stnIds.length > 1 && ( @@ -533,8 +535,8 @@ const PrevStn = (props: { stnIds: string[] }) => { 上一站 - - Past Stop + + {previousText} @@ -542,8 +544,8 @@ const PrevStn = (props: { stnIds: string[] }) => { }; const NextStn = (props: { stnIds: string[] }) => { - const param = useRootSelector(store => store.param); - const nextNames = props.stnIds.map(stnId => param.stn_list[stnId].localisedName); + const { stn_list, direction, svgWidth } = useRootSelector(store => store.param); + const nextNames = props.stnIds.map(stnId => stn_list[stnId].localisedName); const nextHintDy = (props.stnIds.length > 1 ? 15 : 125) + nextNames.map(name => name.zh?.split('\\')?.length ?? 1).reduce((acc, cur) => acc + cur, -nextNames.length) * @@ -558,13 +560,13 @@ const NextStn = (props: { stnIds: string[] }) => { return ( - + {props.stnIds.length > 1 && ( )} @@ -572,7 +574,7 @@ const NextStn = (props: { stnIds: string[] }) => { 下一站 - + Next Stop diff --git a/src/svgs/shmetro/station-shmetro.tsx b/src/svgs/shmetro/station-shmetro.tsx index 25b58c7c9..afe647e5a 100644 --- a/src/svgs/shmetro/station-shmetro.tsx +++ b/src/svgs/shmetro/station-shmetro.tsx @@ -1,5 +1,5 @@ import { ColourHex } from '@railmapgen/rmg-palette-resources'; -import { ExtendedInterchangeInfo, Facilities, InterchangeGroup } from '../../constants/constants'; +import { ExtendedInterchangeInfo, Facilities, InterchangeGroup, PanelTypeShmetro } from '../../constants/constants'; import { useRootSelector } from '../../redux'; import { forwardRef, memo, Ref, SVGProps, useEffect, useMemo, useRef, useState } from 'react'; import { Translation } from '@railmapgen/rmg-translate'; @@ -28,7 +28,34 @@ const StationSHMetro = (props: Props) => { let stationIconStyle: string; const stationIconColor: { [pos: string]: string } = {}; - if (info_panel_type === 'sh2020') { + if (info_panel_type === 'sh2024') { + const int_length = stnInfo.transfer.groups.at(0)?.lines?.length ?? 0; + const osi_osysi_length = [ + ...(stnInfo.transfer.groups.at(1)?.lines || []), + ...(stnInfo.transfer.groups.at(2)?.lines || []), + ].length; + + if (stnInfo.services.length === 3) stationIconStyle = 'stn_sh_2020_direct'; + else if (stnInfo.services.length === 2) stationIconStyle = 'stn_sh_2020_express'; + else if (int_length > 0 && osi_osysi_length === 0) { + // 仅换乘车站 + stationIconStyle = 'stn_sh_2024_int'; + stationIconColor.stroke = stnState === -1 ? 'gray' : color ? color : 'var(--rmg-theme-colour)'; + } else if (int_length > 0 && osi_osysi_length > 0) { + // 站内换乘+出站换乘 + stationIconStyle = 'stn_sh_2024_int_osysi'; + stationIconColor.stroke = stnState === -1 ? 'gray' : color ? color : 'var(--rmg-theme-colour)'; + } else if (int_length === 0 && osi_osysi_length === 1) { + // 仅2线出站换乘 + stationIconStyle = 'stn_sh_2024_osysi2'; + stationIconColor.stroke = stnState === -1 ? 'gray' : color ? color : 'var(--rmg-theme-colour)'; + } else if (int_length === 0 && osi_osysi_length === 2) { + // 仅3线出站换乘 + stationIconStyle = 'stn_sh_2024_osysi3'; + stationIconColor.stroke = stnState === -1 ? 'gray' : color ? color : 'var(--rmg-theme-colour)'; + } else stationIconStyle = 'stn_sh_2020'; + stationIconColor.fill = stnState === -1 ? 'gray' : color ? color : 'var(--rmg-theme-colour)'; + } else if (info_panel_type === 'sh2020') { if (stnInfo.services.length === 3) stationIconStyle = 'stn_sh_2020_direct'; else if (stnInfo.services.length === 2) stationIconStyle = 'stn_sh_2020_express'; else stationIconStyle = 'stn_sh_2020'; @@ -45,7 +72,8 @@ const StationSHMetro = (props: Props) => { const bank = bank_ ?? 0; const dx = (direction === 'l' ? 6 : -6) + branchNameDX + bank * 30; - const dy = (info_panel_type === 'sh2020' ? -20 : -6) + Math.abs(bank) * (info_panel_type === 'sh2020' ? 25 : 11); + const is2020or2024 = info_panel_type === 'sh2020' || info_panel_type === 'sh2024'; + const dy = (is2020or2024 ? -20 : -6) + Math.abs(bank) * (is2020or2024 ? 25 : 11); const dr = bank ? 0 : direction === 'l' ? -45 : 45; return ( <> @@ -90,6 +118,7 @@ interface StationNameGElementProps { const StationNameGElement = (props: StationNameGElementProps) => { const { name, groups, stnState, direction, facility, bank, oneLine, intPadding } = props; + const { info_panel_type } = useRootSelector(store => store.param); // legacy ref to get the exact station name width const stnNameEl = useRef(null); @@ -142,7 +171,7 @@ const StationNameGElement = (props: StationNameGElementProps) => { /> {/* this is out-of-station text displayed above the IntBoxGroup */} - {groups[1]?.lines?.length && ( + {groups[1]?.lines?.length && info_panel_type !== PanelTypeShmetro.sh2024 && ( From 940e6f840b3b68d408606ce47db0c077e3e9740f Mon Sep 17 00:00:00 2001 From: thekingofcity <3353040+thekingofcity@users.noreply.github.com> Date: Fri, 18 Jul 2025 15:44:51 +0800 Subject: [PATCH 02/12] #659 Shanghai Metro railmap canvas 2024 style --- .../station-side-panel/more-section.tsx | 6 +- src/svgs/shmetro/runin-shmetro.tsx | 6 +- src/svgs/shmetro/station-shmetro.tsx | 293 +++++++++++++----- 3 files changed, 230 insertions(+), 75 deletions(-) diff --git a/src/components/side-panel/station-side-panel/more-section.tsx b/src/components/side-panel/station-side-panel/more-section.tsx index 3cf15d8ea..82aff8d4a 100644 --- a/src/components/side-panel/station-side-panel/more-section.tsx +++ b/src/components/side-panel/station-side-panel/more-section.tsx @@ -1,7 +1,7 @@ import { Box, Heading } from '@chakra-ui/react'; import { RmgButtonGroup, RmgFields, RmgFieldsField } from '@railmapgen/rmg-components'; import { useTranslation } from 'react-i18next'; -import { FACILITIES, Facilities, RmgStyle, Services, TEMP } from '../../../constants/constants'; +import { FACILITIES, Facilities, PanelTypeShmetro, RmgStyle, Services, TEMP } from '../../../constants/constants'; import { useRootDispatch, useRootSelector } from '../../../redux'; import { updateStationCharacterSpacing, @@ -20,7 +20,7 @@ export default function MoreSection() { const dispatch = useRootDispatch(); const selectedStation = useRootSelector(state => state.app.selectedStation); - const { style, loop } = useRootSelector(state => state.param); + const { style, loop, info_panel_type } = useRootSelector(state => state.param); const { services, facility, loop_pivot, one_line, int_padding, character_spacing, underConstruction } = useRootSelector(state => state.param.stn_list[selectedStation]); @@ -79,7 +79,7 @@ export default function MoreSection() { label: t('StationSidePanel.more.oneLine'), isChecked: one_line, onChange: checked => dispatch(updateStationOneLine(selectedStation, checked)), - hidden: ![RmgStyle.SHMetro].includes(style), + hidden: !(style === RmgStyle.SHMetro && info_panel_type !== PanelTypeShmetro.sh2024), minW: 'full', oneLine: true, }, diff --git a/src/svgs/shmetro/runin-shmetro.tsx b/src/svgs/shmetro/runin-shmetro.tsx index cf607ae68..04dbeb1ad 100644 --- a/src/svgs/shmetro/runin-shmetro.tsx +++ b/src/svgs/shmetro/runin-shmetro.tsx @@ -1,12 +1,12 @@ /* eslint @typescript-eslint/no-non-null-assertion: 0 */ +import { Translation } from '@railmapgen/rmg-translate'; import { memo, SVGProps, useMemo } from 'react'; -import { CanvasType, StationDict } from '../../constants/constants'; +import { CanvasType, PanelTypeShmetro, StationDict } from '../../constants/constants'; import { useRootSelector } from '../../redux'; import { isColineBranch } from '../../redux/param/coline-action'; import { calculateColineStations } from '../methods/shmetro-coline'; import SvgWrapper from '../svg-wrapper'; import PujiangLineDefs from './pujiang-line-filter'; -import { Translation } from '@railmapgen/rmg-translate'; const LINE_WIDTH = 12; @@ -519,7 +519,7 @@ const PrevStn = (props: { stnIds: string[] }) => { ? (prevZhName.split('\\').length - 1) * -50 + (prevEnName.split('\\').length - 1) * -30 : 0) + 70; - const previousText = info_panel_type === 'sh2024' ? 'Previous Stop' : 'Past Stop'; + const previousText = info_panel_type === PanelTypeShmetro.sh2024 ? 'Previous Stop' : 'Past Stop'; return ( { let stationIconStyle: string; const stationIconColor: { [pos: string]: string } = {}; - if (info_panel_type === 'sh2024') { + if (info_panel_type === PanelTypeShmetro.sh2024) { const int_length = stnInfo.transfer.groups.at(0)?.lines?.length ?? 0; const osi_osysi_length = [ ...(stnInfo.transfer.groups.at(1)?.lines || []), @@ -72,7 +72,7 @@ const StationSHMetro = (props: Props) => { const bank = bank_ ?? 0; const dx = (direction === 'l' ? 6 : -6) + branchNameDX + bank * 30; - const is2020or2024 = info_panel_type === 'sh2020' || info_panel_type === 'sh2024'; + const is2020or2024 = info_panel_type === PanelTypeShmetro.sh2020 || info_panel_type === PanelTypeShmetro.sh2024; const dy = (is2020or2024 ? -20 : -6) + Math.abs(bank) * (is2020or2024 ? 25 : 11); const dr = bank ? 0 : direction === 'l' ? -45 : 45; return ( @@ -132,9 +132,14 @@ const StationNameGElement = (props: StationNameGElementProps) => { // interchange will have a line under the name, and should be stretched when placed horizontal in loop const lineDx = bank ? -12 : 0; + // int group const intEl = useRef(null); const [intWidth, setIntWidth] = useState(0); - useEffect(() => setIntWidth(intEl.current?.getBBox().width ?? 0), [JSON.stringify(groups)]); + useEffect(() => { + // IntBoxGroup2024 will use double render to get the width of the text elements + // so we need to wait and get the int group width + setTimeout(() => setIntWidth(intEl.current?.getBBox().width ?? 0), 10); + }, [JSON.stringify(groups), directionPolarity]); const intDx = intPadding - intWidth; return ( @@ -144,15 +149,27 @@ const StationNameGElement = (props: StationNameGElementProps) => { - + {info_panel_type !== PanelTypeShmetro.sh2024 ? ( + <> + + + ) : ( + + )} )} @@ -165,23 +182,26 @@ const StationNameGElement = (props: StationNameGElementProps) => { - {/* this is out-of-station text displayed above the IntBoxGroup */} - {groups[1]?.lines?.length && info_panel_type !== PanelTypeShmetro.sh2024 && ( - - - - )} - - {/* deal out-of-system here as it's dx is fixed and has nothing to do with IntBoxGroup */} - {groups[2]?.lines?.length && ( - - - + {info_panel_type !== PanelTypeShmetro.sh2024 && ( + <> + {/* this is out-of-station text displayed above the IntBoxGroup */} + {groups[1]?.lines?.length && ( + + + + )} + {/* deal out-of-system here as it's dx is fixed and has nothing to do with IntBoxGroup */} + {groups[2]?.lines?.length && ( + + + + )} + )} @@ -206,38 +226,27 @@ const StationName = forwardRef(function StationName( return ( - {useMemo( - () => ( - <> - - {zhName.split('\\').map((txt, i, arr) => ( - - {txt} - - ))} - - - {enName.split('\\').map((txt, i, arr) => ( - - {txt} - - ))} - - - ), - [zhName, enName, oneLine, enDx, directionPolarity] - )} + + {zhName.split('\\').map((txt, i, arr) => ( + + {txt} + + ))} + + + {enName.split('\\').map((txt, i, arr) => ( + + {txt} + + ))} + ); }); @@ -317,6 +326,119 @@ const IntBoxGroup = forwardRef(function IntBoxGroup( ); }); +const IntBoxGroup2024 = forwardRef(function IntBoxGroup2024( + props: { groups: InterchangeGroup[]; direction: 'l' | 'r'; stnState: -1 | 0 | 1; intPadding: number }, + ref: Ref +) { + const { groups, direction, stnState, intPadding } = props; + const directionPolarity = direction === 'l' ? 1 : -1; + + const transfer = [groups.at(0)?.lines ?? [], groups.at(1)?.lines ?? [], groups.at(2)?.lines ?? []]; + + const [outOfSystemLine, setOutOfSystemLine] = useState([0, 0]); // also for start point of 出站换乘 + const [intBoxesDX, setIntBoxesDX] = useState<{ [k in string]: number }>({}); + const textLineRefs = useRef<{ [k in string]: SVGGElement }>({}); + const [intBoxGroupWidth, setIntBoxGroupWidth] = useState(0); + useEffect(() => { + // update the width of each text line + const textLineWidth = Object.fromEntries( + transfer + .flat() + .filter(info => !info.name[0].match(/^(\d+)号线$/)) + .map(info => { + const key = info.name[0]; + return [key, textLineRefs.current[key]?.getBBox().width ?? 0]; + }) + ); + + const getBBoxWidth = (info: ExtendedInterchangeInfo) => { + const key = info.name[0]; + const lineNumber = key.match(/^(\d+)号线$/); + const boxWidth = lineNumber ? (Number(lineNumber[1]) >= 10 ? 22 : 20) : textLineWidth[info.name[0]]; + intBoxDX[key] = dx * directionPolarity + (lineNumber && direction === 'r' ? -boxWidth : 0); + return boxWidth + 2; + }; + + let dx = 0; // update in every box + const intBoxDX: { [k in string]: number } = {}; + transfer[0].forEach(info => { + dx += getBBoxWidth(info); + }); + let outOfSystemLine = [0, 0]; + if (transfer[1].length) { + if (transfer[0].length) { + outOfSystemLine = [dx, dx + 22]; + dx += 22 * 2; + } else { + dx += 2; // padding + // hide this line if no previous transfer + outOfSystemLine = [dx, dx]; + dx += 22; + } + transfer[1].forEach(info => { + dx += getBBoxWidth(info); + }); + } + transfer[2].forEach(info => { + dx += getBBoxWidth(info); + }); + setIntBoxesDX(intBoxDX); + setOutOfSystemLine(outOfSystemLine); + setIntBoxGroupWidth(dx); + }, [JSON.stringify(transfer), direction]); + + const makeBoxElement = (info: ExtendedInterchangeInfo) => { + const key = info.name[0]; + const isLineNumber = Boolean(key.match(/^(\d+)号线$/)); + return ( + { + if (el && !isLineNumber) textLineRefs.current[key] = el; + }} + transform={`translate(${intBoxesDX[key] ?? 0},-11)`} + > + {isLineNumber ? ( + + ) : ( + + )} + + ); + }; + + const intBoxDX = (intPadding - intBoxGroupWidth) * directionPolarity; + return ( + + {transfer[0].map(makeBoxElement)} + {transfer[1].length && ( + <> + {transfer[0].length > 0 && ( + + )} + + 出站 + 换乘 + + {transfer[1].map(makeBoxElement)} + + )} + {transfer[2].map(makeBoxElement)} + + ); +}); + const IntBoxMaglev = memo( function IntBoxMaglev(props: { info: ExtendedInterchangeInfo }) { return ( @@ -343,6 +465,22 @@ const IntBoxNumber = memo( (prevProps, nextProps) => JSON.stringify(prevProps.info) === JSON.stringify(nextProps.info) ); +const IntBoxNumber2024 = (props: { info: ExtendedInterchangeInfo }) => { + const { + info: { name, theme }, + } = props; + const num = name[0].match(/^(\d+)号线$/)?.[1] ?? ''; + const width = num.length > 1 ? 22 : 18; // 22 for 10+ lines, 18 for 1-9 lines + return ( + + + + {num} + + + ); +}; + const IntBoxLetter = memo( function IntBoxLetter(props: { info: ExtendedInterchangeInfo }) { // box width: 16 * number of characters + 12 @@ -364,6 +502,26 @@ const IntBoxLetter = memo( (prevProps, nextProps) => JSON.stringify(prevProps.info) === JSON.stringify(nextProps.info) ); +const IntBoxText2024 = (props: { info: ExtendedInterchangeInfo; state: -1 | 0 | 1; direction: 'l' | 'r' }) => { + const { + info: { name }, + state, + direction, + } = props; + return ( + + {name[0]} + {name[1]} + + ); +}; + const OSIText = (props: { osiInfos: ExtendedInterchangeInfo[] }) => { // get the all names from the out of station interchanges const lineNames = props.osiInfos.map(info => info.name[0]).join(','); @@ -390,17 +548,14 @@ const OSysIText = (props: { osysiInfos: ExtendedInterchangeInfo[]; direction: 'l const lineNames = props.osysiInfos.map(info => info.name[0]).join(','); const lineNamesEn = props.osysiInfos.map(info => info.name[1]).join(', '); - return useMemo( - () => ( - - - 转乘{lineNames} - - - To {lineNamesEn} - - - ), - [JSON.stringify(props.osysiInfos), props.direction] + return ( + + + 转乘{lineNames} + + + To {lineNamesEn} + + ); }; From dcb3d292de066283ae6b0a49f57dad013a5d5a27 Mon Sep 17 00:00:00 2001 From: thekingofcity <3353040+thekingofcity@users.noreply.github.com> Date: Sat, 19 Jul 2025 17:30:01 +0800 Subject: [PATCH 03/12] stn in middle of the railmap --- src/svgs/shmetro/railmap-shmetro.tsx | 26 +++++++++++++------------- src/svgs/shmetro/station-shmetro.tsx | 10 ++++------ 2 files changed, 17 insertions(+), 19 deletions(-) diff --git a/src/svgs/shmetro/railmap-shmetro.tsx b/src/svgs/shmetro/railmap-shmetro.tsx index 04034fdb3..4740d18a9 100644 --- a/src/svgs/shmetro/railmap-shmetro.tsx +++ b/src/svgs/shmetro/railmap-shmetro.tsx @@ -66,25 +66,25 @@ const DefsSHMetro = memo(function DefsSHMetro() { id="stn_sh_2024_int" fill="var(--rmg-white)" strokeWidth={2} - d="M 0,-12 a 5,5 0 1 1 10,0 V0 a 5,5 0 1 1 -10,0 Z" + d="M -5,-12 a 5,5 0 1 1 10,0 V0 a 5,5 0 1 1 -10,0 Z" /> - - + + - - - - + + + + - - - - - - + + + + + + diff --git a/src/svgs/shmetro/station-shmetro.tsx b/src/svgs/shmetro/station-shmetro.tsx index 3cdd4aba3..6025adaac 100644 --- a/src/svgs/shmetro/station-shmetro.tsx +++ b/src/svgs/shmetro/station-shmetro.tsx @@ -55,7 +55,7 @@ const StationSHMetro = (props: Props) => { stationIconColor.stroke = stnState === -1 ? 'gray' : color ? color : 'var(--rmg-theme-colour)'; } else stationIconStyle = 'stn_sh_2020'; stationIconColor.fill = stnState === -1 ? 'gray' : color ? color : 'var(--rmg-theme-colour)'; - } else if (info_panel_type === 'sh2020') { + } else if (info_panel_type === PanelTypeShmetro.sh2020) { if (stnInfo.services.length === 3) stationIconStyle = 'stn_sh_2020_direct'; else if (stnInfo.services.length === 2) stationIconStyle = 'stn_sh_2020_express'; else stationIconStyle = 'stn_sh_2020'; @@ -71,7 +71,8 @@ const StationSHMetro = (props: Props) => { } const bank = bank_ ?? 0; - const dx = (direction === 'l' ? 6 : -6) + branchNameDX + bank * 30; + const dx2024 = info_panel_type === PanelTypeShmetro.sh2024 ? (direction === 'l' ? -5 : 5) : 0; + const dx = (direction === 'l' ? 6 : -6) + branchNameDX + bank * 30 + dx2024; const is2020or2024 = info_panel_type === PanelTypeShmetro.sh2020 || info_panel_type === PanelTypeShmetro.sh2024; const dy = (is2020or2024 ? -20 : -6) + Math.abs(bank) * (is2020or2024 ? 25 : 11); const dr = bank ? 0 : direction === 'l' ? -45 : 45; @@ -81,10 +82,7 @@ const StationSHMetro = (props: Props) => { xlinkHref={`#${stationIconStyle}`} {...stationIconColor} // different styles use either `fill` or `stroke` // sh and sh2020 have different headings of int_sh, so -1 | 1 is multiplied - transform={ - `translate(${bank * (info_panel_type === 'sh2020' ? 5 : 0)},0)` + - `rotate(${bank * 90 * (info_panel_type === 'sh2020' ? 1 : -1)})` - } + transform={`translate(${bank * (is2020or2024 ? 5 : 0)},0)rotate(${bank * 90 * (is2020or2024 ? 1 : -1)})`} /> Date: Sat, 19 Jul 2025 20:57:09 +0800 Subject: [PATCH 04/12] fine tune of int box width --- src/svgs/shmetro/station-shmetro.tsx | 51 ++++++++++++++++++++-------- 1 file changed, 37 insertions(+), 14 deletions(-) diff --git a/src/svgs/shmetro/station-shmetro.tsx b/src/svgs/shmetro/station-shmetro.tsx index 6025adaac..da6f40a35 100644 --- a/src/svgs/shmetro/station-shmetro.tsx +++ b/src/svgs/shmetro/station-shmetro.tsx @@ -4,6 +4,15 @@ import { forwardRef, memo, Ref, SVGProps, useEffect, useMemo, useRef, useState } import { ExtendedInterchangeInfo, Facilities, InterchangeGroup, PanelTypeShmetro } from '../../constants/constants'; import { useRootSelector } from '../../redux'; +const INT_BOX_SIZE = { + width: { + singleDigit: 19.8, + doubleDigit: 24.2, + }, + height: 22, + padding: 2, +}; + interface Props { stnId: string; stnState: -1 | 0 | 1; @@ -352,9 +361,13 @@ const IntBoxGroup2024 = forwardRef(function IntBoxGroup2024( const getBBoxWidth = (info: ExtendedInterchangeInfo) => { const key = info.name[0]; const lineNumber = key.match(/^(\d+)号线$/); - const boxWidth = lineNumber ? (Number(lineNumber[1]) >= 10 ? 22 : 20) : textLineWidth[info.name[0]]; + const boxWidth = lineNumber + ? Number(lineNumber[1]) >= 10 + ? INT_BOX_SIZE.width.doubleDigit + : INT_BOX_SIZE.width.singleDigit + : textLineWidth[info.name[0]]; intBoxDX[key] = dx * directionPolarity + (lineNumber && direction === 'r' ? -boxWidth : 0); - return boxWidth + 2; + return boxWidth + INT_BOX_SIZE.padding; }; let dx = 0; // update in every box @@ -364,14 +377,17 @@ const IntBoxGroup2024 = forwardRef(function IntBoxGroup2024( }); let outOfSystemLine = [0, 0]; if (transfer[1].length) { + // there will be a line and a text element for 出站换乘 + // each will take 22px + const elementWidth = INT_BOX_SIZE.height; if (transfer[0].length) { - outOfSystemLine = [dx, dx + 22]; - dx += 22 * 2; + outOfSystemLine = [dx, dx + elementWidth]; + dx += elementWidth * 2; } else { - dx += 2; // padding - // hide this line if no previous transfer + dx += INT_BOX_SIZE.padding; + // hide this line if there is no previous transfer outOfSystemLine = [dx, dx]; - dx += 22; + dx += elementWidth; } transfer[1].forEach(info => { dx += getBBoxWidth(info); @@ -394,7 +410,7 @@ const IntBoxGroup2024 = forwardRef(function IntBoxGroup2024( ref={el => { if (el && !isLineNumber) textLineRefs.current[key] = el; }} - transform={`translate(${intBoxesDX[key] ?? 0},-11)`} + transform={`translate(${intBoxesDX[key] ?? 0},${-INT_BOX_SIZE.height / 2})`} > {isLineNumber ? ( @@ -407,7 +423,7 @@ const IntBoxGroup2024 = forwardRef(function IntBoxGroup2024( const intBoxDX = (intPadding - intBoxGroupWidth) * directionPolarity; return ( - + {transfer[0].map(makeBoxElement)} {transfer[1].length && ( <> @@ -422,7 +438,7 @@ const IntBoxGroup2024 = forwardRef(function IntBoxGroup2024( @@ -468,11 +484,18 @@ const IntBoxNumber2024 = (props: { info: ExtendedInterchangeInfo }) => { info: { name, theme }, } = props; const num = name[0].match(/^(\d+)号线$/)?.[1] ?? ''; - const width = num.length > 1 ? 22 : 18; // 22 for 10+ lines, 18 for 1-9 lines + const width = num.length > 1 ? INT_BOX_SIZE.width.doubleDigit : INT_BOX_SIZE.width.singleDigit; + const letterSpacing = num.length > 1 ? -2.5 : 0; return ( - - + + {num} @@ -512,7 +535,7 @@ const IntBoxText2024 = (props: { info: ExtendedInterchangeInfo; state: -1 | 0 | fill={state < 0 ? 'gray' : 'black'} dominantBaseline="central" textAnchor={direction === 'l' ? 'start' : 'end'} - fontSize="50%" + fontSize="7" > {name[0]} {name[1]} From 915f4b9fdcfc2d2d43c6f9f99c4ca09a095eeb48 Mon Sep 17 00:00:00 2001 From: thekingofcity <3353040+thekingofcity@users.noreply.github.com> Date: Sat, 19 Jul 2025 21:43:06 +0800 Subject: [PATCH 05/12] #656 Shanghai Metro indoor canvas 2024 style --- src/svgs/shmetro/indoor/indoor-shmetro.tsx | 38 ++- src/svgs/shmetro/indoor/station-shmetro.tsx | 291 +++++++++++++++----- src/svgs/shmetro/station-shmetro.tsx | 17 +- 3 files changed, 272 insertions(+), 74 deletions(-) diff --git a/src/svgs/shmetro/indoor/indoor-shmetro.tsx b/src/svgs/shmetro/indoor/indoor-shmetro.tsx index f9c453120..97ba92c75 100644 --- a/src/svgs/shmetro/indoor/indoor-shmetro.tsx +++ b/src/svgs/shmetro/indoor/indoor-shmetro.tsx @@ -2,7 +2,7 @@ import { memo, useMemo } from 'react'; import { adjacencyList, criticalPathMethod, getStnState, getXShareMTR } from '../../methods/share'; import StationSHMetro from './station-shmetro'; import { StationsSHMetro } from '../../methods/mtr'; -import { CanvasType, Services, StationDict } from '../../../constants/constants'; +import { CanvasType, PanelTypeShmetro, Services, StationDict } from '../../../constants/constants'; import { useRootSelector } from '../../../redux'; import LoopSHMetro from '../loop/loop-shmetro'; import SvgWrapper from '../../svg-wrapper'; @@ -11,7 +11,13 @@ const CANVAS_TYPE = CanvasType.Indoor; export default function IndoorWrapperSHMetro() { const { canvasScale } = useRootSelector(state => state.app); - const { svgWidth: svgWidths, svg_height: svgHeight, theme, loop } = useRootSelector(store => store.param); + const { + svgWidth: svgWidths, + svg_height: svgHeight, + theme, + loop, + info_panel_type, + } = useRootSelector(store => store.param); const svgWidth = svgWidths[CANVAS_TYPE]; @@ -25,7 +31,7 @@ export default function IndoorWrapperSHMetro() { > {loop ? : } - + {info_panel_type !== PanelTypeShmetro.sh2024 && } ); } @@ -61,6 +67,32 @@ export const DefsSHMetro = memo(function DefsSHMetro() { + + + + + + + + + + + + + + + + + + + + + ); }); diff --git a/src/svgs/shmetro/indoor/station-shmetro.tsx b/src/svgs/shmetro/indoor/station-shmetro.tsx index 0d1f9eb6b..98a70ae55 100644 --- a/src/svgs/shmetro/indoor/station-shmetro.tsx +++ b/src/svgs/shmetro/indoor/station-shmetro.tsx @@ -1,8 +1,9 @@ import { ColourHex } from '@railmapgen/rmg-palette-resources'; +import { Translation } from '@railmapgen/rmg-translate'; import { Fragment, Ref, SVGProps, forwardRef, useEffect, useMemo, useRef, useState } from 'react'; -import { ExtendedInterchangeInfo, InterchangeGroup, Services } from '../../../constants/constants'; +import { ExtendedInterchangeInfo, InterchangeGroup, PanelTypeShmetro, Services } from '../../../constants/constants'; import { useRootSelector } from '../../../redux'; -import { Translation } from '@railmapgen/rmg-translate'; +import { INT_BOX_SIZE, IntBoxNumber2024, IntBoxText2024 } from '../station-shmetro'; /** * Which direction to display station name. Currently shmetro only. @@ -18,15 +19,53 @@ interface Props { export const StationSHMetro = (props: Props) => { const { stnId, nameDirection, services, color } = props; - const stnInfo = useRootSelector(store => store.param.stn_list[stnId]); + const { stn_list, info_panel_type } = useRootSelector(store => store.param); + const stnInfo = stn_list[stnId]; - const transfer = [...(stnInfo.transfer.groups[0]?.lines || []), ...(stnInfo.transfer.groups[1]?.lines || [])]; let stationIconStyle: string; - if (stnInfo.services.length === 3) stationIconStyle = 'direct_indoor_sh'; - else if (stnInfo.services.length === 2) stationIconStyle = 'express_indoor_sh'; - else if (stnInfo.transfer.groups[1]?.lines?.length ?? 0 > 0) stationIconStyle = 'osi_indoor_sh'; - else if (transfer.length > 0) stationIconStyle = 'int2_indoor_sh'; - else stationIconStyle = 'stn_indoor_sh'; + const stationIconColor: { [pos: string]: string } = {}; + if (info_panel_type === PanelTypeShmetro.sh2024) { + const int_length = stnInfo.transfer.groups.at(0)?.lines?.length ?? 0; + const osi_osysi_length = [ + ...(stnInfo.transfer.groups.at(1)?.lines || []), + ...(stnInfo.transfer.groups.at(2)?.lines || []), + ].length; + + stationIconColor.stroke = 'var(--rmg-theme-colour)'; + if (stnInfo.services.length === 3) { + stationIconStyle = 'stn_sh_2020_direct'; + } else if (stnInfo.services.length === 2) { + stationIconStyle = 'stn_sh_2020_express'; + } else if (int_length > 0 && osi_osysi_length === 0) { + // 仅换乘车站 + stationIconStyle = 'stn_sh_2024_int'; + } else if (int_length > 0 && osi_osysi_length > 0) { + // 站内换乘+出站换乘 + stationIconStyle = 'stn_sh_2024_int_osysi'; + } else if (int_length === 0 && osi_osysi_length === 1) { + // 仅2线出站换乘 + stationIconStyle = 'stn_sh_2024_osysi2'; + } else if (int_length === 0 && osi_osysi_length === 2) { + // 仅3线出站换乘 + stationIconStyle = 'stn_sh_2024_osysi3'; + } else { + stationIconStyle = 'stn_sh_2024'; + delete stationIconColor.stroke; + stationIconColor.fill = 'var(--rmg-theme-colour)'; + } + } else { + const non_osysi_transfer = [ + ...(stnInfo.transfer.groups[0]?.lines || []), + ...(stnInfo.transfer.groups[1]?.lines || []), + ]; + if (stnInfo.services.length === 3) stationIconStyle = 'direct_indoor_sh'; + else if (stnInfo.services.length === 2) stationIconStyle = 'express_indoor_sh'; + else if (stnInfo.transfer.groups[1]?.lines?.length ?? 0 > 0) stationIconStyle = 'osi_indoor_sh'; + else if (non_osysi_transfer.length > 0) stationIconStyle = 'int2_indoor_sh'; + else stationIconStyle = 'stn_indoor_sh'; + stationIconColor.stroke = + non_osysi_transfer.length > 0 ? 'var(--rmg-black)' : (color ?? 'var(--rmg-theme-colour)'); + } const dr = nameDirection === 'left' || nameDirection === 'right' ? 90 : 0; return ( @@ -39,8 +78,8 @@ export const StationSHMetro = (props: Props) => { /> 0 ? 'var(--rmg-black)' : (color ?? 'var(--rmg-theme-colour)')} transform={`rotate(${dr})`} + {...stationIconColor} // different styles use either `fill` or `stroke` /> {/* This should be in IntBoxGroupProps, put here because the station icon will cover this */} {stnInfo.services.length > 1 && ( @@ -63,6 +102,7 @@ interface StationNameGElementProps { const StationNameGElement = (props: StationNameGElementProps) => { const { name, groups, nameDirection, services } = props; + const { info_panel_type } = useRootSelector(store => store.param); const dy = { upward: 60, downward: -30, left: 0, right: 0 }[nameDirection]; const osysi_dx = // only compute when there is an out-of-system transfer @@ -106,36 +146,45 @@ const StationNameGElement = (props: StationNameGElementProps) => { : 0; const nameRef = useRef(null); const MIN_NAME_LINE_LENGTH = 60; - const [nameWidth, setNameWidth] = useState(MIN_NAME_LINE_LENGTH); + const [nameSize, setNameSize] = useState({ width: MIN_NAME_LINE_LENGTH, height: 0 }); useEffect(() => { - if (nameRef?.current) setNameWidth(Math.max(MIN_NAME_LINE_LENGTH, nameRef.current.getBBox().width)); + if (nameRef?.current) setNameSize(nameRef.current.getBBox()); }, [name.zh, name.en]); return ( {nameDirection === 'upward' || nameDirection === 'downward' ? ( <> + {info_panel_type !== PanelTypeShmetro.sh2024 && ( + + )} - ) : ( <> - + {info_panel_type !== PanelTypeShmetro.sh2024 && ( + + )} { )} - {[...(groups[0].lines || []), ...(groups[1]?.lines || [])].length && ( - + {info_panel_type !== PanelTypeShmetro.sh2024 ? ( + [...(groups[0].lines || []), ...(groups[1]?.lines || [])].length && ( + + ) + ) : ( + )} - {groups[2]?.lines?.length && ( + {info_panel_type !== PanelTypeShmetro.sh2024 && groups[2]?.lines?.length && ( @@ -184,36 +237,26 @@ const StationName = forwardRef(function StationName( return ( - {useMemo( - () => ( - <> - {name.map((txt, i, array) => ( - - {txt} - - ))} - - {enName.split('\\')?.map((txt, i) => ( - 1 ? name.length * 7.5 : 0) : 0) - } - > - {txt} - - ))} - - - ), - [zhName, enName] - )} + {name.map((txt, i, array) => ( + + {txt} + + ))} + + {enName.split('\\')?.map((txt, i) => ( + 1 ? name.length * 7.5 : 0) : 0)} + > + {txt} + + ))} + ); }); @@ -349,6 +392,126 @@ const IntBoxGroup = (props: IntBoxGroupProps & SVGProps) => { ); }; +const IntBoxGroup2024 = forwardRef(function IntBoxGroup2024( + props: { groups: InterchangeGroup[]; dy: number }, + ref: Ref +) { + const { groups, dy } = props; + const directionPolarity = 1; + + const transfer = [groups.at(0)?.lines ?? [], groups.at(1)?.lines ?? [], groups.at(2)?.lines ?? []]; + + const [outOfSystemLine, setOutOfSystemLine] = useState(0); // also for start point of 出站换乘 + const [intBoxesDX, setIntBoxesDX] = useState<{ [k in string]: number }>({}); + const textLineRefs = useRef<{ [k in string]: SVGGElement }>({}); + const [intBoxGroupWidth, setIntBoxGroupWidth] = useState(0); + useEffect(() => { + // update the width of each text line + const textLineWidth = Object.fromEntries( + transfer + .flat() + .filter(info => !info.name[0].match(/^(\d+)号线$/)) + .map(info => { + const key = info.name[0]; + return [key, textLineRefs.current[key]?.getBBox().width ?? 0]; + }) + ); + + const getBBoxWidth = (info: ExtendedInterchangeInfo) => { + const key = info.name[0]; + const lineNumber = key.match(/^(\d+)号线$/); + const boxWidth = lineNumber + ? Number(lineNumber[1]) >= 10 + ? INT_BOX_SIZE.width.doubleDigit + : INT_BOX_SIZE.width.singleDigit + : textLineWidth[info.name[0]]; + intBoxDX[key] = dx * directionPolarity; + return boxWidth + INT_BOX_SIZE.padding; + }; + + let dx = 0; // update in every box + const intBoxDX: { [k in string]: number } = {}; + transfer[0].forEach(info => { + dx += getBBoxWidth(info); + }); + let outOfStationLine = 0; + if (transfer[1].length) { + // there will be a line and a text element for 出站换乘 + // each will take 22px + const elementWidth = INT_BOX_SIZE.height; + if (transfer[0].length) { + outOfStationLine = dx + INT_BOX_SIZE.padding; + dx += elementWidth + 2 * INT_BOX_SIZE.padding; + } else { + dx += INT_BOX_SIZE.padding; + // hide this line if there is no previous transfer + outOfStationLine = 0; + dx += elementWidth; + } + transfer[1].forEach(info => { + dx += getBBoxWidth(info); + }); + } + transfer[2].forEach(info => { + dx += getBBoxWidth(info); + }); + setIntBoxesDX(intBoxDX); + setOutOfSystemLine(outOfStationLine); + setIntBoxGroupWidth(dx); + }, [JSON.stringify(transfer)]); + + const makeBoxElement = (info: ExtendedInterchangeInfo) => { + const key = info.name[0]; + const isLineNumber = Boolean(key.match(/^(\d+)号线$/)); + return ( + { + if (el && !isLineNumber) textLineRefs.current[key] = el; + }} + transform={`translate(${intBoxesDX[key] ?? 0},${-INT_BOX_SIZE.height / 2})`} + > + {isLineNumber ? ( + + ) : ( + + )} + + ); + }; + + return ( + + {transfer[0].map(makeBoxElement)} + {transfer[1].length && ( + <> + {transfer[0].length > 0 && ( + + )} + + 出站 + 换乘 + + {transfer[1].map(makeBoxElement)} + + )} + {transfer[2].map(makeBoxElement)} + + ); +}); + const OSysIText = (props: { osysiInfos: ExtendedInterchangeInfo[]; nameDirection: NameDirection }) => { const anchor = { upward: 'middle', downward: 'middle', left: 'start', right: 'end' }[props.nameDirection]; return useMemo( diff --git a/src/svgs/shmetro/station-shmetro.tsx b/src/svgs/shmetro/station-shmetro.tsx index da6f40a35..b38cb6321 100644 --- a/src/svgs/shmetro/station-shmetro.tsx +++ b/src/svgs/shmetro/station-shmetro.tsx @@ -4,7 +4,7 @@ import { forwardRef, memo, Ref, SVGProps, useEffect, useMemo, useRef, useState } import { ExtendedInterchangeInfo, Facilities, InterchangeGroup, PanelTypeShmetro } from '../../constants/constants'; import { useRootSelector } from '../../redux'; -const INT_BOX_SIZE = { +export const INT_BOX_SIZE = { width: { singleDigit: 19.8, doubleDigit: 24.2, @@ -438,7 +438,7 @@ const IntBoxGroup2024 = forwardRef(function IntBoxGroup2024( @@ -479,7 +479,7 @@ const IntBoxNumber = memo( (prevProps, nextProps) => JSON.stringify(prevProps.info) === JSON.stringify(nextProps.info) ); -const IntBoxNumber2024 = (props: { info: ExtendedInterchangeInfo }) => { +export const IntBoxNumber2024 = (props: { info: ExtendedInterchangeInfo }) => { const { info: { name, theme }, } = props; @@ -523,7 +523,7 @@ const IntBoxLetter = memo( (prevProps, nextProps) => JSON.stringify(prevProps.info) === JSON.stringify(nextProps.info) ); -const IntBoxText2024 = (props: { info: ExtendedInterchangeInfo; state: -1 | 0 | 1; direction: 'l' | 'r' }) => { +export const IntBoxText2024 = (props: { info: ExtendedInterchangeInfo; state: -1 | 0 | 1; direction: 'l' | 'r' }) => { const { info: { name }, state, @@ -535,10 +535,13 @@ const IntBoxText2024 = (props: { info: ExtendedInterchangeInfo; state: -1 | 0 | fill={state < 0 ? 'gray' : 'black'} dominantBaseline="central" textAnchor={direction === 'l' ? 'start' : 'end'} - fontSize="7" > - {name[0]} - {name[1]} + + {name[0]} + + + {name[1]} + ); }; From 40879452a9c81f2d16df7b6e016762f4d03495de Mon Sep 17 00:00:00 2001 From: thekingofcity <3353040+thekingofcity@users.noreply.github.com> Date: Sun, 20 Jul 2025 20:12:33 +0800 Subject: [PATCH 06/12] fine tune --- src/svgs/shmetro/main-shmetro.tsx | 96 +++++++++++++--------------- src/svgs/shmetro/runin-shmetro.tsx | 10 +-- src/svgs/shmetro/station-shmetro.tsx | 34 +++++++--- 3 files changed, 77 insertions(+), 63 deletions(-) diff --git a/src/svgs/shmetro/main-shmetro.tsx b/src/svgs/shmetro/main-shmetro.tsx index d4b88b782..e72996036 100644 --- a/src/svgs/shmetro/main-shmetro.tsx +++ b/src/svgs/shmetro/main-shmetro.tsx @@ -1,7 +1,7 @@ import { adjacencyList, criticalPathMethod, drawLine, getStnState, getXShareMTR } from '../methods/share'; import StationSHMetro from './station-shmetro'; import ColineSHMetro from './coline-shmetro'; -import { AtLeastOneOfPartial, Services, StationDict } from '../../constants/constants'; +import { AtLeastOneOfPartial, PanelTypeShmetro, Services, StationDict } from '../../constants/constants'; import { useRootSelector } from '../../redux'; import { useMemo } from 'react'; @@ -425,64 +425,60 @@ const ServicesElements = (props: { servicesLevel: Services[]; lineXs: number[] } const dx_hint = props.servicesLevel.length === 2 ? 350 : 500; - return useMemo( - () => ( - - {servicesLevel.map((service, i) => ( - - - {`${service}运行线`} + return ( + + {servicesLevel.map((service, i) => ( + + + {`${service}运行线`} + + ))} + + 图例: + {servicesLevel.map((serviceLevel, i) => ( + + + + {`${serviceLevel}停靠站`} ))} - - 图例: - {servicesLevel.map((serviceLevel, i) => ( - - - - {`${serviceLevel}停靠站`} - - ))} - - ), - [svg_height, direction, svgWidth, props.servicesLevel, props.lineXs] + ); }; export const DirectionElements = () => { - const { direction, svgWidth, coline } = useRootSelector(store => store.param); + const { direction, svgWidth, coline, info_panel_type } = useRootSelector(store => store.param); // arrow will be black stroke with white fill in coline const isColine = !!Object.keys(coline).length; - return useMemo( - () => ( - - 列车前进方向 - - - ), - [direction, coline, svgWidth.railmap] + const arrowColor = + info_panel_type === PanelTypeShmetro.sh2024 + ? 'var(--rmg-black)' + : !isColine + ? 'var(--rmg-theme-colour)' + : 'var(--rmg-white)'; + + return ( + + 列车前进方向 + + ); }; diff --git a/src/svgs/shmetro/runin-shmetro.tsx b/src/svgs/shmetro/runin-shmetro.tsx index 04dbeb1ad..efd24e725 100644 --- a/src/svgs/shmetro/runin-shmetro.tsx +++ b/src/svgs/shmetro/runin-shmetro.tsx @@ -519,7 +519,7 @@ const PrevStn = (props: { stnIds: string[] }) => { ? (prevZhName.split('\\').length - 1) * -50 + (prevEnName.split('\\').length - 1) * -30 : 0) + 70; - const previousText = info_panel_type === PanelTypeShmetro.sh2024 ? 'Previous Stop' : 'Past Stop'; + const previousTextEn = info_panel_type === PanelTypeShmetro.sh2024 ? 'Previous Station' : 'Past Stop'; return ( { 上一站 - {previousText} + {previousTextEn} @@ -544,7 +544,7 @@ const PrevStn = (props: { stnIds: string[] }) => { }; const NextStn = (props: { stnIds: string[] }) => { - const { stn_list, direction, svgWidth } = useRootSelector(store => store.param); + const { stn_list, direction, svgWidth, info_panel_type } = useRootSelector(store => store.param); const nextNames = props.stnIds.map(stnId => stn_list[stnId].localisedName); const nextHintDy = (props.stnIds.length > 1 ? 15 : 125) + @@ -558,6 +558,8 @@ const NextStn = (props: { stnIds: string[] }) => { ? (nextZhName.split('\\').length - 1) * -50 + (nextEnName.split('\\').length - 1) * -30 : 0) + 70; + const nextTextEn = info_panel_type === PanelTypeShmetro.sh2024 ? 'Next Station' : 'Next Stop'; + return ( { 下一站 - Next Stop + {nextTextEn} diff --git a/src/svgs/shmetro/station-shmetro.tsx b/src/svgs/shmetro/station-shmetro.tsx index b38cb6321..e8567ff96 100644 --- a/src/svgs/shmetro/station-shmetro.tsx +++ b/src/svgs/shmetro/station-shmetro.tsx @@ -58,7 +58,7 @@ const StationSHMetro = (props: Props) => { // 仅2线出站换乘 stationIconStyle = 'stn_sh_2024_osysi2'; stationIconColor.stroke = stnState === -1 ? 'gray' : color ? color : 'var(--rmg-theme-colour)'; - } else if (int_length === 0 && osi_osysi_length === 2) { + } else if (int_length === 0 && osi_osysi_length >= 2) { // 仅3线出站换乘 stationIconStyle = 'stn_sh_2024_osysi3'; stationIconColor.stroke = stnState === -1 ? 'gray' : color ? color : 'var(--rmg-theme-colour)'; @@ -105,7 +105,7 @@ const StationSHMetro = (props: Props) => { intPadding={stnInfo.int_padding} /> - {stnState === 0 ? : undefined} + {stnState === 0 && } ); }; @@ -191,6 +191,7 @@ const StationNameGElement = (props: StationNameGElementProps) => { stnName={name} oneLine={info_panel_type === PanelTypeShmetro.sh2024 ? true : oneLine} directionPolarity={directionPolarity} + stnState={stnState} fill={stnState === -1 ? 'gray' : stnState === 0 ? 'red' : 'black'} /> @@ -216,11 +217,17 @@ const StationNameGElement = (props: StationNameGElementProps) => { }; const StationName = forwardRef(function StationName( - props: { stnName: Translation; oneLine: boolean; directionPolarity: 1 | -1 } & SVGProps, + props: { + stnName: Translation; + oneLine: boolean; + directionPolarity: 1 | -1; + stnState: -1 | 0 | 1; + } & SVGProps, ref: Ref ) { - const { stnName, oneLine, directionPolarity, ...others } = props; + const { stnName, oneLine, directionPolarity, stnState, ...others } = props; const { zh: zhName = '', en: enName = '' } = stnName; + const { info_panel_type } = useRootSelector(store => store.param); const zhEl = useRef(null); const [enDx, setEnDx] = useState(0); @@ -231,9 +238,14 @@ const StationName = forwardRef(function StationName( const [ZH_HEIGHT, EN_HEIGHT] = [20, 8]; + const fontSize = { + zh: info_panel_type === PanelTypeShmetro.sh2024 && !stnState ? 18 : 16, + en: info_panel_type === PanelTypeShmetro.sh2024 && !stnState ? 8 : 9, + }; + return ( - + {zhName.split('\\').map((txt, i, arr) => ( ))} - + {enName.split('\\').map((txt, i, arr) => ( {txt} @@ -259,17 +271,21 @@ const StationName = forwardRef(function StationName( }); const CurrentStationText = () => { - const { stn_list } = useRootSelector(store => store.param); + const { stn_list, info_panel_type } = useRootSelector(store => store.param); const servicesPresent = new Set( Object.values(stn_list) .map(stn => stn.services) .flat() ); - const dy = [-1, 35, 50, 75][servicesPresent.size]; + const dy = { + [PanelTypeShmetro.sh]: [-1, 35, 45, 70], + [PanelTypeShmetro.sh2020]: [-1, 20, 45, 70], + [PanelTypeShmetro.sh2024]: [-1, 20, 45, 70], + }[info_panel_type as PanelTypeShmetro][servicesPresent.size]; return ( - + 本站 From abc09804c52d637d72bc537f86d12d5ca1889244 Mon Sep 17 00:00:00 2001 From: thekingofcity <3353040+thekingofcity@users.noreply.github.com> Date: Sun, 20 Jul 2025 20:13:37 +0800 Subject: [PATCH 07/12] update translation --- src/i18n/translations/en.json | 3 ++- src/i18n/translations/ja.json | 3 ++- src/i18n/translations/ko.json | 1 + src/i18n/translations/zh-Hans.json | 3 ++- src/i18n/translations/zh-Hant.json | 3 ++- 5 files changed, 9 insertions(+), 4 deletions(-) diff --git a/src/i18n/translations/en.json b/src/i18n/translations/en.json index 9a71177a7..60ea30da3 100644 --- a/src/i18n/translations/en.json +++ b/src/i18n/translations/en.json @@ -86,7 +86,8 @@ "gz1421": "2017 style", "gz7w": "2022 style", "sh": "Default", - "sh2020": "2020 (Beta)", + "sh2020": "2020", + "sh2024": "2024 (Beta)", "nameDisplay": "Display station names", "alternatively": "Alternatively", "onOneSide": "Same side", diff --git a/src/i18n/translations/ja.json b/src/i18n/translations/ja.json index 663e1c9bc..12e3d894a 100644 --- a/src/i18n/translations/ja.json +++ b/src/i18n/translations/ja.json @@ -84,7 +84,8 @@ "gz1421": "2017風格", "gz7w": "2022風格", "sh": "デフォルト", - "sh2020": "2020風格(プレビュー版)", + "sh2020": "2020風格", + "sh2024": "2024風格(プレビュー版)", "nameDisplay": "駅名表示", "alternatively": "交互表示", "onOneSide": "片側表示", diff --git a/src/i18n/translations/ko.json b/src/i18n/translations/ko.json index 6c9ec4086..6dc6181a5 100644 --- a/src/i18n/translations/ko.json +++ b/src/i18n/translations/ko.json @@ -84,6 +84,7 @@ "gz7w": "2022년 모양", "sh": "묵인", "sh2020": "2020모양(사전 검토)", + "sh2024": "2024모양", "nameDisplay": "역 명칭 보이기", "alternatively": "교차 표시", "onOneSide": "같은 쪽", diff --git a/src/i18n/translations/zh-Hans.json b/src/i18n/translations/zh-Hans.json index 171e863d5..37d573ecb 100644 --- a/src/i18n/translations/zh-Hans.json +++ b/src/i18n/translations/zh-Hans.json @@ -84,7 +84,8 @@ "gz1421": "2017样式", "gz7w": "2022样式", "sh": "默认", - "sh2020": "2020样式(预览版)", + "sh2020": "2020样式", + "sh2024": "2024样式(预览版)", "nameDisplay": "显示车站名称", "alternatively": "交错显示", "onOneSide": "同一侧", diff --git a/src/i18n/translations/zh-Hant.json b/src/i18n/translations/zh-Hant.json index 6bd308d59..d42fbf7b3 100644 --- a/src/i18n/translations/zh-Hant.json +++ b/src/i18n/translations/zh-Hant.json @@ -79,7 +79,8 @@ "gz1421": "2017樣式", "gz7w": "2022樣式", "sh": "預設", - "sh2020": "2020樣式(預覽版)", + "sh2020": "2020樣式", + "sh2024": "2024樣式(預覽版)", "nameDisplay": "顯示車站名稱", "alternatively": "交錯顯示", "onOneSide": "同一側", From e092f55a182d516faa6eaedc1a665ae42b52d9ec Mon Sep 17 00:00:00 2001 From: thekingofcity <3353040+thekingofcity@users.noreply.github.com> Date: Mon, 21 Jul 2025 17:12:48 +0800 Subject: [PATCH 08/12] fine tune --- src/svgs/shmetro/indoor/station-shmetro.tsx | 141 +++++++++++++++++--- src/svgs/shmetro/station-shmetro.tsx | 9 +- 2 files changed, 127 insertions(+), 23 deletions(-) diff --git a/src/svgs/shmetro/indoor/station-shmetro.tsx b/src/svgs/shmetro/indoor/station-shmetro.tsx index 98a70ae55..f23759222 100644 --- a/src/svgs/shmetro/indoor/station-shmetro.tsx +++ b/src/svgs/shmetro/indoor/station-shmetro.tsx @@ -1,9 +1,14 @@ import { ColourHex } from '@railmapgen/rmg-palette-resources'; import { Translation } from '@railmapgen/rmg-translate'; import { Fragment, Ref, SVGProps, forwardRef, useEffect, useMemo, useRef, useState } from 'react'; -import { ExtendedInterchangeInfo, InterchangeGroup, PanelTypeShmetro, Services } from '../../../constants/constants'; +import { + ExtendedInterchangeInfo, + InterchangeGroup, + PanelTypeGZMTR, + PanelTypeShmetro, + Services, +} from '../../../constants/constants'; import { useRootSelector } from '../../../redux'; -import { INT_BOX_SIZE, IntBoxNumber2024, IntBoxText2024 } from '../station-shmetro'; /** * Which direction to display station name. Currently shmetro only. @@ -93,6 +98,25 @@ export const StationSHMetro = (props: Props) => { export default StationSHMetro; +const stationNameDY = ( + info_panel_type: PanelTypeShmetro | PanelTypeGZMTR, + nameDirection: NameDirection, + nameENLn: number +) => { + if (info_panel_type === PanelTypeShmetro.sh2024) { + if (nameDirection === 'upward') return 5; + if (nameDirection === 'downward') return -45 - 12 * (nameENLn - 1); + if (nameDirection === 'left' || nameDirection === 'right') return -10 * (nameENLn - 1); + } else { + if (nameDirection === 'upward') return -2; + if (nameDirection === 'downward') return -30 - 12 * (nameENLn - 1); + if (nameDirection === 'left' || nameDirection === 'right') return -10 * (nameENLn - 1); + } + return 0; +}; + +const STATION_NAME_2024_ZH_FONT_SIZE = 22; + interface StationNameGElementProps { name: Translation; groups: InterchangeGroup[]; @@ -102,6 +126,8 @@ interface StationNameGElementProps { const StationNameGElement = (props: StationNameGElementProps) => { const { name, groups, nameDirection, services } = props; + const { en: enName = '' } = name; + const nameENLn = enName.split('\\').length; const { info_panel_type } = useRootSelector(store => store.param); const dy = { upward: 60, downward: -30, left: 0, right: 0 }[nameDirection]; const osysi_dx = @@ -146,10 +172,27 @@ const StationNameGElement = (props: StationNameGElementProps) => { : 0; const nameRef = useRef(null); const MIN_NAME_LINE_LENGTH = 60; - const [nameSize, setNameSize] = useState({ width: MIN_NAME_LINE_LENGTH, height: 0 }); + const [nameSize, setNameSize] = useState({ width: MIN_NAME_LINE_LENGTH, height: 0, x: 0, y: 0 }); useEffect(() => { if (nameRef?.current) setNameSize(nameRef.current.getBBox()); }, [name.zh, name.en]); + + const panel_type = info_panel_type as PanelTypeShmetro; + const verticalLineY = { + [PanelTypeShmetro.sh2024]: { upward: -15, downward: -30 }, + [PanelTypeShmetro.sh2020]: { upward: -23, downward: -10 }, + [PanelTypeShmetro.sh]: { upward: -23, downward: -10 }, + }; + const intBoxGroup2024DY = { + upward: stationNameDY(info_panel_type, nameDirection, nameENLn) + nameSize.height + INT_BOX_SIZE.height / 2, + downward: + stationNameDY(info_panel_type, nameDirection, nameENLn) - + STATION_NAME_2024_ZH_FONT_SIZE / 2 - + INT_BOX_SIZE.height / 2, + left: 7, + right: 7, + }; + return ( {nameDirection === 'upward' || nameDirection === 'downward' ? ( @@ -158,13 +201,13 @@ const StationNameGElement = (props: StationNameGElementProps) => { )} { /> ) ) : ( - + )} @@ -222,19 +265,20 @@ const StationName = forwardRef(function StationName( props: { stnName: Translation; nameDirection: NameDirection } & SVGProps, ref: Ref ) { + const { info_panel_type } = useRootSelector(store => store.param); const { stnName, nameDirection, ...others } = props; const { zh: zhName = '', en: enName = '' } = stnName; const name = zhName.split('\\'); const nameENLn = enName.split('\\').length; const dx = { upward: 0, downward: 0, left: -60, right: 60 }[nameDirection]; - const dy = { - upward: -2, - downward: -30 - 12 * (nameENLn - 1), - left: -10 * (nameENLn - 1), - right: -10 * (nameENLn - 1), - }[nameDirection]; + const dy = stationNameDY(info_panel_type, nameDirection, nameENLn); const anchor = { upward: 'middle', downward: 'middle', left: 'end', right: 'start' }[nameDirection]; + const fontSize = { + zh: info_panel_type === PanelTypeShmetro.sh2024 ? STATION_NAME_2024_ZH_FONT_SIZE : 16, + en: info_panel_type === PanelTypeShmetro.sh2024 ? 11 : 9.6, + }; + return ( {name.map((txt, i, array) => ( @@ -242,11 +286,12 @@ const StationName = forwardRef(function StationName( key={i} className="rmg-name__zh" dy={nameDirection === 'upward' ? 16 * i : (array.length - 1 - i) * -16} + fontSize={fontSize.zh} > {txt} ))} - + {enName.split('\\')?.map((txt, i) => ( { const key = info.name[0]; const isLineNumber = Boolean(key.match(/^(\d+)号线$/)); @@ -474,14 +530,15 @@ const IntBoxGroup2024 = forwardRef(function IntBoxGroup2024( {isLineNumber ? ( ) : ( - + )} ); }; + const dx = onlyOneTextInt ? 0 : -intBoxGroupWidth / 2; return ( - + {transfer[0].map(makeBoxElement)} {transfer[1].length && ( <> @@ -497,12 +554,12 @@ const IntBoxGroup2024 = forwardRef(function IntBoxGroup2024( - 出站 - 换乘 + 出站 + 换乘 {transfer[1].map(makeBoxElement)} @@ -512,6 +569,52 @@ const IntBoxGroup2024 = forwardRef(function IntBoxGroup2024( ); }); +const IntBoxNumber2024 = (props: { info: ExtendedInterchangeInfo }) => { + const { + info: { name, theme }, + } = props; + const num = name[0].match(/^(\d+)号线$/)?.[1] ?? ''; + const width = num.length > 1 ? INT_BOX_SIZE.width.doubleDigit : INT_BOX_SIZE.width.singleDigit; + const letterSpacing = num.length > 1 ? -3 : 0; + return ( + + + + {num} + + + ); +}; + +const IntBoxText2024 = (props: { info: ExtendedInterchangeInfo; onlyOne: boolean }) => { + const { + info: { name }, + onlyOne, + } = props; + return ( + + + {name[0]} + + + {name[1]} + + + ); +}; + const OSysIText = (props: { osysiInfos: ExtendedInterchangeInfo[]; nameDirection: NameDirection }) => { const anchor = { upward: 'middle', downward: 'middle', left: 'start', right: 'end' }[props.nameDirection]; return useMemo( diff --git a/src/svgs/shmetro/station-shmetro.tsx b/src/svgs/shmetro/station-shmetro.tsx index e8567ff96..82030267d 100644 --- a/src/svgs/shmetro/station-shmetro.tsx +++ b/src/svgs/shmetro/station-shmetro.tsx @@ -4,7 +4,7 @@ import { forwardRef, memo, Ref, SVGProps, useEffect, useMemo, useRef, useState } import { ExtendedInterchangeInfo, Facilities, InterchangeGroup, PanelTypeShmetro } from '../../constants/constants'; import { useRootSelector } from '../../redux'; -export const INT_BOX_SIZE = { +const INT_BOX_SIZE = { width: { singleDigit: 19.8, doubleDigit: 24.2, @@ -46,6 +46,7 @@ const StationSHMetro = (props: Props) => { if (stnInfo.services.length === 3) stationIconStyle = 'stn_sh_2020_direct'; else if (stnInfo.services.length === 2) stationIconStyle = 'stn_sh_2020_express'; + // TODO: 不管多少条站内换乘,只要有超过1个的出站换乘就是3个圆了 else if (int_length > 0 && osi_osysi_length === 0) { // 仅换乘车站 stationIconStyle = 'stn_sh_2024_int'; @@ -58,7 +59,7 @@ const StationSHMetro = (props: Props) => { // 仅2线出站换乘 stationIconStyle = 'stn_sh_2024_osysi2'; stationIconColor.stroke = stnState === -1 ? 'gray' : color ? color : 'var(--rmg-theme-colour)'; - } else if (int_length === 0 && osi_osysi_length >= 2) { + } else if (int_length === 0 && osi_osysi_length === 2) { // 仅3线出站换乘 stationIconStyle = 'stn_sh_2024_osysi3'; stationIconColor.stroke = stnState === -1 ? 'gray' : color ? color : 'var(--rmg-theme-colour)'; @@ -495,7 +496,7 @@ const IntBoxNumber = memo( (prevProps, nextProps) => JSON.stringify(prevProps.info) === JSON.stringify(nextProps.info) ); -export const IntBoxNumber2024 = (props: { info: ExtendedInterchangeInfo }) => { +const IntBoxNumber2024 = (props: { info: ExtendedInterchangeInfo }) => { const { info: { name, theme }, } = props; @@ -539,7 +540,7 @@ const IntBoxLetter = memo( (prevProps, nextProps) => JSON.stringify(prevProps.info) === JSON.stringify(nextProps.info) ); -export const IntBoxText2024 = (props: { info: ExtendedInterchangeInfo; state: -1 | 0 | 1; direction: 'l' | 'r' }) => { +const IntBoxText2024 = (props: { info: ExtendedInterchangeInfo; state: -1 | 0 | 1; direction: 'l' | 'r' }) => { const { info: { name }, state, From 0116c920b545459e8808057835dc87c3b029295f Mon Sep 17 00:00:00 2001 From: thekingofcity <3353040+thekingofcity@users.noreply.github.com> Date: Wed, 23 Jul 2025 14:21:00 +0800 Subject: [PATCH 09/12] fine tune --- src/svgs/shmetro/indoor/indoor-shmetro.tsx | 35 +++++++++++++++++++-- src/svgs/shmetro/indoor/station-shmetro.tsx | 23 ++++++++------ src/svgs/shmetro/station-shmetro.tsx | 11 +++---- 3 files changed, 52 insertions(+), 17 deletions(-) diff --git a/src/svgs/shmetro/indoor/indoor-shmetro.tsx b/src/svgs/shmetro/indoor/indoor-shmetro.tsx index 97ba92c75..fa268663a 100644 --- a/src/svgs/shmetro/indoor/indoor-shmetro.tsx +++ b/src/svgs/shmetro/indoor/indoor-shmetro.tsx @@ -36,7 +36,7 @@ export default function IndoorWrapperSHMetro() { ); } -export const DefsSHMetro = memo(function DefsSHMetro() { +const DefsSHMetro = memo(function DefsSHMetro() { return ( @@ -121,7 +121,7 @@ const IndoorSHMetro = () => { const realCP = criticalPathMethod(criticalPath.nodes[1], criticalPath.nodes.slice(-2)[0], adjMat); const xShares = useMemo(() => { - console.log('computing x shares'); + console.debug('computing x shares'); return Object.keys(param.stn_list).reduce( (acc, cur) => ({ ...acc, [cur]: getXShareMTR(cur, adjMat, branches) }), {} as { [stnId: string]: number } @@ -174,10 +174,16 @@ const IndoorSHMetro = () => { 0 ); + const lineBadgeDX = Math.min( + ...Object.entries(xs) + .filter(([k]) => !['linestart', 'lineend'].includes(k)) + .map(([, v]) => v) + ); return ( + {param.info_panel_type === PanelTypeShmetro.sh2024 && } ); }; @@ -318,6 +324,31 @@ const InfoElements = memo(() => { InfoElements.displayName = 'InfoElements'; +const LineBadge = (props: { dx: number }) => { + const { dx } = props; + const { line_name, theme } = useRootSelector(store => store.param); + const num = line_name[0].match(/^(\d+)号线$/)?.[1] ?? ''; + const width = num.length > 1 ? 20 : 15; + const letterSpacing = num.length > 1 ? -1.5 : 0; + const padding = 15; + return ( + + + + {num} + + + ); +}; + /* Some unused functions to split branches from the main line. * Note the branches here has a slightly different meaning. * It refers to all the line sections that have a parallel line diff --git a/src/svgs/shmetro/indoor/station-shmetro.tsx b/src/svgs/shmetro/indoor/station-shmetro.tsx index f23759222..063381f11 100644 --- a/src/svgs/shmetro/indoor/station-shmetro.tsx +++ b/src/svgs/shmetro/indoor/station-shmetro.tsx @@ -41,6 +41,9 @@ export const StationSHMetro = (props: Props) => { stationIconStyle = 'stn_sh_2020_direct'; } else if (stnInfo.services.length === 2) { stationIconStyle = 'stn_sh_2020_express'; + } else if (osi_osysi_length > 1) { + // 不管多少条站内换乘,只要有超过1个的出站换乘就是3个圆了 + stationIconStyle = 'stn_sh_2024_osysi3'; } else if (int_length > 0 && osi_osysi_length === 0) { // 仅换乘车站 stationIconStyle = 'stn_sh_2024_int'; @@ -50,9 +53,6 @@ export const StationSHMetro = (props: Props) => { } else if (int_length === 0 && osi_osysi_length === 1) { // 仅2线出站换乘 stationIconStyle = 'stn_sh_2024_osysi2'; - } else if (int_length === 0 && osi_osysi_length === 2) { - // 仅3线出站换乘 - stationIconStyle = 'stn_sh_2024_osysi3'; } else { stationIconStyle = 'stn_sh_2024'; delete stationIconColor.stroke; @@ -308,7 +308,7 @@ const StationName = forwardRef(function StationName( const INT_BOX_SIZE = { width: { - singleDigit: 19.8, + singleDigit: 27, doubleDigit: 33, }, height: 30, @@ -514,7 +514,12 @@ const IntBoxGroup2024 = forwardRef(function IntBoxGroup2024( setIntBoxGroupWidth(dx); }, [JSON.stringify(transfer)]); - const onlyOneTextInt = transfer.flat().length === 1 && !transfer.flat()[0].name[0].match(/^(\d+)号线$/); + // only in case of one non out-of-station text line transfer, the text will be centered + const nonOutOfStationTransfer = [groups.at(0)?.lines ?? [], groups.at(2)?.lines ?? []]; + const onlyOneNonOutOfStationTextInt = + transfer.flat().length === 1 && + nonOutOfStationTransfer.flat().length === 1 && + !nonOutOfStationTransfer.flat()[0].name[0].match(/^(\d+)号线$/); const makeBoxElement = (info: ExtendedInterchangeInfo) => { const key = info.name[0]; @@ -530,13 +535,13 @@ const IntBoxGroup2024 = forwardRef(function IntBoxGroup2024( {isLineNumber ? ( ) : ( - + )} ); }; - const dx = onlyOneTextInt ? 0 : -intBoxGroupWidth / 2; + const dx = onlyOneNonOutOfStationTextInt ? 0 : -intBoxGroupWidth / 2; return ( {transfer[0].map(makeBoxElement)} @@ -605,10 +610,10 @@ const IntBoxText2024 = (props: { info: ExtendedInterchangeInfo; onlyOne: boolean dominantBaseline="central" textAnchor={onlyOne ? 'middle' : 'start'} > - + {name[0]} - + {name[1]} diff --git a/src/svgs/shmetro/station-shmetro.tsx b/src/svgs/shmetro/station-shmetro.tsx index 82030267d..0eeb0322a 100644 --- a/src/svgs/shmetro/station-shmetro.tsx +++ b/src/svgs/shmetro/station-shmetro.tsx @@ -46,8 +46,11 @@ const StationSHMetro = (props: Props) => { if (stnInfo.services.length === 3) stationIconStyle = 'stn_sh_2020_direct'; else if (stnInfo.services.length === 2) stationIconStyle = 'stn_sh_2020_express'; - // TODO: 不管多少条站内换乘,只要有超过1个的出站换乘就是3个圆了 - else if (int_length > 0 && osi_osysi_length === 0) { + else if (osi_osysi_length > 1) { + // 不管多少条站内换乘,只要有超过1个的出站换乘就是3个圆了 + stationIconStyle = 'stn_sh_2024_osysi3'; + stationIconColor.stroke = stnState === -1 ? 'gray' : color ? color : 'var(--rmg-theme-colour)'; + } else if (int_length > 0 && osi_osysi_length === 0) { // 仅换乘车站 stationIconStyle = 'stn_sh_2024_int'; stationIconColor.stroke = stnState === -1 ? 'gray' : color ? color : 'var(--rmg-theme-colour)'; @@ -59,10 +62,6 @@ const StationSHMetro = (props: Props) => { // 仅2线出站换乘 stationIconStyle = 'stn_sh_2024_osysi2'; stationIconColor.stroke = stnState === -1 ? 'gray' : color ? color : 'var(--rmg-theme-colour)'; - } else if (int_length === 0 && osi_osysi_length === 2) { - // 仅3线出站换乘 - stationIconStyle = 'stn_sh_2024_osysi3'; - stationIconColor.stroke = stnState === -1 ? 'gray' : color ? color : 'var(--rmg-theme-colour)'; } else stationIconStyle = 'stn_sh_2020'; stationIconColor.fill = stnState === -1 ? 'gray' : color ? color : 'var(--rmg-theme-colour)'; } else if (info_panel_type === PanelTypeShmetro.sh2020) { From ac7344ea7e0ca837bd339a08cb2ecf952fadc134 Mon Sep 17 00:00:00 2001 From: thekingofcity <3353040+thekingofcity@users.noreply.github.com> Date: Wed, 23 Jul 2025 19:17:14 +0800 Subject: [PATCH 10/12] fine tune --- src/svgs/shmetro/indoor/indoor-shmetro.tsx | 9 +++++---- src/svgs/shmetro/indoor/station-shmetro.tsx | 2 +- 2 files changed, 6 insertions(+), 5 deletions(-) diff --git a/src/svgs/shmetro/indoor/indoor-shmetro.tsx b/src/svgs/shmetro/indoor/indoor-shmetro.tsx index fa268663a..63b3934db 100644 --- a/src/svgs/shmetro/indoor/indoor-shmetro.tsx +++ b/src/svgs/shmetro/indoor/indoor-shmetro.tsx @@ -328,20 +328,21 @@ const LineBadge = (props: { dx: number }) => { const { dx } = props; const { line_name, theme } = useRootSelector(store => store.param); const num = line_name[0].match(/^(\d+)号线$/)?.[1] ?? ''; - const width = num.length > 1 ? 20 : 15; + const width = num.length > 1 ? 26.4 : 21.6; + const textDX = num.length > 1 ? 12 : 11; const letterSpacing = num.length > 1 ? -1.5 : 0; const padding = 15; return ( - + {num} diff --git a/src/svgs/shmetro/indoor/station-shmetro.tsx b/src/svgs/shmetro/indoor/station-shmetro.tsx index 063381f11..5b6bca2dc 100644 --- a/src/svgs/shmetro/indoor/station-shmetro.tsx +++ b/src/svgs/shmetro/indoor/station-shmetro.tsx @@ -585,7 +585,7 @@ const IntBoxNumber2024 = (props: { info: ExtendedInterchangeInfo }) => { Date: Thu, 24 Jul 2025 14:31:44 +0800 Subject: [PATCH 11/12] fine tune --- src/svgs/shmetro/indoor/indoor-shmetro.tsx | 23 ++++++++++++++++++++-- src/svgs/shmetro/runin-shmetro.tsx | 21 +++++++++----------- 2 files changed, 30 insertions(+), 14 deletions(-) diff --git a/src/svgs/shmetro/indoor/indoor-shmetro.tsx b/src/svgs/shmetro/indoor/indoor-shmetro.tsx index 63b3934db..8a8f88fbc 100644 --- a/src/svgs/shmetro/indoor/indoor-shmetro.tsx +++ b/src/svgs/shmetro/indoor/indoor-shmetro.tsx @@ -31,7 +31,7 @@ export default function IndoorWrapperSHMetro() { > {loop ? : } - {info_panel_type !== PanelTypeShmetro.sh2024 && } + {info_panel_type !== PanelTypeShmetro.sh2024 ? : } ); } @@ -324,12 +324,31 @@ const InfoElements = memo(() => { InfoElements.displayName = 'InfoElements'; +const InfoElements2024 = () => { + const { + svg_height, + svgWidth: { indoor: svg_width }, + } = useRootSelector(store => store.param); + + return ( + + + 友情提示:请留意您需要换乘线路的首末班时间,以免耽误您的出行,末班车进站前三分钟停售该末班车车票。 + + + Please pay attention to the interchange schedule if you want to transfer to other lines. Stop selling + tickets 3 minutes before the last train services. + + + ); +}; + const LineBadge = (props: { dx: number }) => { const { dx } = props; const { line_name, theme } = useRootSelector(store => store.param); const num = line_name[0].match(/^(\d+)号线$/)?.[1] ?? ''; const width = num.length > 1 ? 26.4 : 21.6; - const textDX = num.length > 1 ? 12 : 11; + const textDX = num.length > 1 ? 0 : 4; const letterSpacing = num.length > 1 ? -1.5 : 0; const padding = 15; return ( diff --git a/src/svgs/shmetro/runin-shmetro.tsx b/src/svgs/shmetro/runin-shmetro.tsx index efd24e725..e6899b20c 100644 --- a/src/svgs/shmetro/runin-shmetro.tsx +++ b/src/svgs/shmetro/runin-shmetro.tsx @@ -453,18 +453,15 @@ const CurrentText = () => { const param = useRootSelector(store => store.param); const { localisedName } = param.stn_list[param.current_stn_idx]; const { zh: zhName = '', en: enName = '' } = localisedName; - return useMemo( - () => ( - <> - - {zhName.replace('\\', '')} - - - {enName.replace('\\', '')} - - - ), - [zhName, enName] + return ( + <> + + {zhName.replace('\\', '')} + + + {enName.replace('\\', '')} + + ); }; From 39f2d9679d8a5c9d6cede213f466f118203498bf Mon Sep 17 00:00:00 2001 From: thekingofcity <3353040+thekingofcity@users.noreply.github.com> Date: Mon, 1 Sep 2025 17:37:34 +0800 Subject: [PATCH 12/12] =?UTF-8?q?revert=20=E4=B8=8A=E4=B8=80=E7=AB=99=20Pr?= =?UTF-8?q?evious=20Station?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/svgs/shmetro/runin-shmetro.tsx | 27 +++++++++++++++++++-------- 1 file changed, 19 insertions(+), 8 deletions(-) diff --git a/src/svgs/shmetro/runin-shmetro.tsx b/src/svgs/shmetro/runin-shmetro.tsx index e6899b20c..6816687bb 100644 --- a/src/svgs/shmetro/runin-shmetro.tsx +++ b/src/svgs/shmetro/runin-shmetro.tsx @@ -516,8 +516,6 @@ const PrevStn = (props: { stnIds: string[] }) => { ? (prevZhName.split('\\').length - 1) * -50 + (prevEnName.split('\\').length - 1) * -30 : 0) + 70; - const previousTextEn = info_panel_type === PanelTypeShmetro.sh2024 ? 'Previous Station' : 'Past Stop'; - return ( { )} - - 上一站 - - - {previousTextEn} - + {info_panel_type === PanelTypeShmetro.sh2024 ? ( + <> + + 上一站 + + + Previous Station + + + ) : ( + <> + + 上一站 + + + Past Stop + + + )} );