From 1ab90611e2514222b38af59c136785daf61ed520 Mon Sep 17 00:00:00 2001 From: Riku Oja Date: Tue, 11 Mar 2025 17:42:00 +0200 Subject: [PATCH 1/5] Remove line duplicates and order line numbers --- web/src/components/InfoSlider.tsx | 67 ++++++++++++++++++++++++------- web/src/utils/utils.ts | 35 ++++++++++++++++ 2 files changed, 87 insertions(+), 15 deletions(-) diff --git a/web/src/components/InfoSlider.tsx b/web/src/components/InfoSlider.tsx index 1b917f4..25c91f8 100644 --- a/web/src/components/InfoSlider.tsx +++ b/web/src/components/InfoSlider.tsx @@ -39,7 +39,7 @@ import SwipeableViews from "react-swipeable-views"; import palette from "../theme/palette"; import shadows from "../theme/shadows"; import { DataSource, gqlPattern, PopupInfo } from "../types"; -import { getCategoryIcon, getCategoryPlural } from "../utils/utils"; +import { compareParts, getCategoryIcon, getCategoryPlural } from "../utils/utils"; import { useElementSize } from "../utils/UseElementSize"; import PropertyListItem from "./PropertyListItem"; import { LayerId } from "./style"; @@ -236,6 +236,56 @@ export default function InfoSlider({ popupInfo }: PopupProps) { return null; }; + /** + * Render GraphQL patterns + * + * Only filter and sort line numbers when rendering. Most of the time, + * the user does not click on any stop. Makes no sense to do any of this + * until we are actually rendering the list for a specific stop. + */ + const renderPatterns = (patterns: Array) => { + return + {patterns.reduce>( + (unique: Array, pattern: gqlPattern) => + // Don't display identical lines with the same destinations + unique.some((other: gqlPattern) => + pattern["route"]["shortName"] == other["route"]["shortName"] && + pattern["headsign"] == other["headsign"] + ) ? unique : [...unique, pattern], + [] // Initial array value + ).sort( + (pattern: gqlPattern, other: gqlPattern) => { + // Order first by first part, second by second part + // Note that first and second parts may both be number *or* letter. + const line_parts = pattern["route"]["shortName"].match('([0-9]+|[A-Z]+)( )?([0-9]*[A-Z]*)') + const other_line_parts = other["route"]["shortName"].match('([0-9]+|[A-Z]+)( )?([0-9]*[A-Z]*)') + if (line_parts && other_line_parts) { + // part 0 is the whole match, parts 1 to 3 are capture groups + const first = line_parts[1] + const other_first = other_line_parts[1] + const second = line_parts.length == 4 ? line_parts[3] : "" + const other_second = other_line_parts.length == 4 ? other_line_parts[3] : "" + const compare_first = compareParts(first, other_first) + return compare_first ? compare_first : compareParts(second, other_second) + } + return line_parts ? -1 : 0 + } + ).map( + (value: gqlPattern, index: number) => ( + + + + {value["route"]["shortName"]} + + + + + ) + )} + + }; + + /** * Basic information slide */ @@ -313,20 +363,7 @@ export default function InfoSlider({ popupInfo }: PopupProps) { {properties["patterns"] && ( <> Linjat - - {JSON.parse(properties["patterns"]).map( - (value: gqlPattern, index: number) => ( - - - - {value["route"]["shortName"]} - - - - - ) - )} - + {renderPatterns(JSON.parse(properties["patterns"]))} )} diff --git a/web/src/utils/utils.ts b/web/src/utils/utils.ts index bd908e1..de1d07f 100644 --- a/web/src/utils/utils.ts +++ b/web/src/utils/utils.ts @@ -1,6 +1,41 @@ import { FeatureCollection, Feature } from "geojson"; import { Category, gqlStop, gqlBikeStation, gqlResponse, stopType } from "../types"; +/** + * Compare string parts that may be strings or strigified integers. Returns integers + * (sorted numerically) before strings (sorted alphabetically). + * + * @param one String that may or may not represent an integer + * @param other String that may or may not represent an integer + * @returns + */ +export const compareParts = (one: string, other: string) => { + const one_number = parseInt(one) + const other_number = parseInt(other) + // compare numbers + if (one_number && other_number) { + return one_number - other_number + } + if (one_number) { + return -1 + } + if (other_number) { + return 1 + } + // compare strings + if (one && other) { + return one.localeCompare(other) + } + // empty strings before strings + if (one) { + return 1 + } + if (other) { + return -1 + } + return 0 +} + export const buildQuery = ( gqlQuery: string, params: Map From faa90af40c2d8d055dd4f7beb5bbf7f6dd8273c0 Mon Sep 17 00:00:00 2001 From: Riku Oja Date: Tue, 11 Mar 2025 17:52:29 +0200 Subject: [PATCH 2/5] Fix train station type based on current ids --- web/src/utils/utils.ts | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/web/src/utils/utils.ts b/web/src/utils/utils.ts index de1d07f..794e881 100644 --- a/web/src/utils/utils.ts +++ b/web/src/utils/utils.ts @@ -62,7 +62,9 @@ export const parseStop = (gqlFeature: gqlStop): Feature => { type = stopType.Tram; tarmo_category = "Ratikkapysäkki"; } - if (gqlFeature.gtfsId.startsWith("TampereVR")) { + // Nowadays, train station ids seem to have the format digitraffic:ABC_1, + // while bus and tram stop ids have the format tampere:xxxx. + if (gqlFeature.gtfsId.startsWith("digitraffic:")) { type = stopType.Train; tarmo_category = "Rautatieasema"; } From 9bfe861b25571995ea1f20df657927d24ef42fd1 Mon Sep 17 00:00:00 2001 From: Riku Oja Date: Tue, 11 Mar 2025 18:35:55 +0200 Subject: [PATCH 3/5] Remove public transport shelters from shelter map --- .../versions/3d38553ecec1/downgrade.sql | 10 +++++ .../alembic/versions/3d38553ecec1/upgrade.sql | 10 +++++ ...3ecec1_remove_public_transport_shelters.py | 37 +++++++++++++++++++ backend/databasemodel/model.sql | 2 +- 4 files changed, 58 insertions(+), 1 deletion(-) create mode 100644 backend/databasemodel/alembic/versions/3d38553ecec1/downgrade.sql create mode 100644 backend/databasemodel/alembic/versions/3d38553ecec1/upgrade.sql create mode 100644 backend/databasemodel/alembic/versions/3d38553ecec1_remove_public_transport_shelters.py diff --git a/backend/databasemodel/alembic/versions/3d38553ecec1/downgrade.sql b/backend/databasemodel/alembic/versions/3d38553ecec1/downgrade.sql new file mode 100644 index 0000000..286028c --- /dev/null +++ b/backend/databasemodel/alembic/versions/3d38553ecec1/downgrade.sql @@ -0,0 +1,10 @@ +DELETE FROM kooste.osm_metadata; +INSERT INTO kooste.osm_metadata (tags_to_include, tags_to_exclude) +VALUES ( + '{"amenity": ["parking", "bicycle_parking", "bbq", "bench", "cafe", "ice_cream", "recycling", "restaurant", "shelter", "toilets", "waste_basket"], + "tourism": ["camp_site", "caravan_site", "chalet", "guest_house", "hostel", "hotel", "information", "motel", "museum", "picnic_site", "viewpoint", "wilderness_hut"], + "leisure": ["bird_hide", "picnic_table", "sauna"], + "shop": ["kiosk"], + "building": ["church"]}', + '{"access": ["private", "permit"]}' + ); diff --git a/backend/databasemodel/alembic/versions/3d38553ecec1/upgrade.sql b/backend/databasemodel/alembic/versions/3d38553ecec1/upgrade.sql new file mode 100644 index 0000000..dc5ae1a --- /dev/null +++ b/backend/databasemodel/alembic/versions/3d38553ecec1/upgrade.sql @@ -0,0 +1,10 @@ +DELETE FROM kooste.osm_metadata; +INSERT INTO kooste.osm_metadata (tags_to_include, tags_to_exclude) +VALUES ( + '{"amenity": ["parking", "bicycle_parking", "bbq", "bench", "cafe", "ice_cream", "recycling", "restaurant", "shelter", "toilets", "waste_basket"], + "tourism": ["camp_site", "caravan_site", "chalet", "guest_house", "hostel", "hotel", "information", "motel", "museum", "picnic_site", "viewpoint", "wilderness_hut"], + "leisure": ["bird_hide", "picnic_table", "sauna"], + "shop": ["kiosk"], + "building": ["church"]}', + '{"access": ["private", "permit"], "shelter_type": ["public_transport"]}' + ); diff --git a/backend/databasemodel/alembic/versions/3d38553ecec1_remove_public_transport_shelters.py b/backend/databasemodel/alembic/versions/3d38553ecec1_remove_public_transport_shelters.py new file mode 100644 index 0000000..6bc2569 --- /dev/null +++ b/backend/databasemodel/alembic/versions/3d38553ecec1_remove_public_transport_shelters.py @@ -0,0 +1,37 @@ +"""remove public transport shelters + +Revision ID: 3d38553ecec1 +Revises: 9f3e55c9d75f +Create Date: 2025-03-11 18:22:40.100843 + +""" +import os + +from alembic import op + +here = os.path.dirname(os.path.realpath(__file__)) + +# revision identifiers, used by Alembic. +revision = "3d38553ecec1" +down_revision = "9f3e55c9d75f" +branch_labels = None +depends_on = None + +revision_dir = f"{here}/{revision}" + + +# idea from https://github.com/tbobm/alembic-sequeled +def process_migration(script_name: str): + filename = f"{revision_dir}/{script_name}.sql" + + query = "\n".join(open(filename)) + if len(query) > 0: + op.execute(query) + + +def upgrade(): + process_migration("upgrade") + + +def downgrade(): + process_migration("downgrade") diff --git a/backend/databasemodel/model.sql b/backend/databasemodel/model.sql index 71c06bd..345eeca 100644 --- a/backend/databasemodel/model.sql +++ b/backend/databasemodel/model.sql @@ -1462,7 +1462,7 @@ INSERT INTO kooste.osm_metadata ( "leisure": ["bird_hide", "picnic_table", "sauna"], "shop": ["kiosk"], "building": ["church"]}', - '{"access": ["private", "permit"]}' + '{"access": ["private", "permit"], "shelter_type": ["public_transport"]}' ); -- object: kooste.tamperewfs_luonnonmuistomerkit | type: TABLE -- From 11ea36943d76476f8dbfcf9a06cf2a3254b4ba6c Mon Sep 17 00:00:00 2001 From: Riku Oja Date: Tue, 11 Mar 2025 18:44:51 +0200 Subject: [PATCH 4/5] Bump mypy to v1.15 --- .pre-commit-config.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml index 53e91c3..70d1903 100644 --- a/.pre-commit-config.yaml +++ b/.pre-commit-config.yaml @@ -36,7 +36,7 @@ repos: hooks: - id: black - repo: https://github.com/pre-commit/mirrors-mypy - rev: v0.910-1 + rev: v1.15.0 hooks: - id: mypy additional_dependencies: From a0272bc6fdae6142c1afe10966f2abeb3882af52 Mon Sep 17 00:00:00 2001 From: Riku Oja Date: Tue, 11 Mar 2025 19:12:15 +0200 Subject: [PATCH 5/5] Fix eslint react version until react is upgraded --- web/package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/web/package.json b/web/package.json index 79d7780..278e061 100644 --- a/web/package.json +++ b/web/package.json @@ -121,7 +121,7 @@ "settings": { "react": { "pragma": "React", - "version": "detect" + "version": "17.0.2" }, "import/parsers": { "@typescript-eslint/parser": [