From 1654de59d27f344488dc676e334db2ac1c192205 Mon Sep 17 00:00:00 2001 From: Tsubasa SEKIGUCHI Date: Sun, 16 Nov 2025 20:56:49 +0900 Subject: [PATCH 1/6] master<-dev (#1337) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * GetRoutesMinimal RPCを実装 (#1334) * Add minimal route retrieval functionality and related proto types * Refactor code formatting for improved readability in grpc.rs and query.rs * Add line symbols to LineMinimal in get_lines_by_station_group_id * Update subproject commit reference in stationapi/proto * Add get_lines_by_id_list method and update QueryUseCase trait * Refactor import order for improved readability in grpc.rs * Use entry method for inserting line_minimal in all_lines to avoid overwriting * Refactor route_row_tree_map creation into a separate build_route_tree_map method for improved readability * Enhance line symbol handling in QueryInteractor: populate line_symbols for all lines and update line_minimal entries conditionally * Refactor line symbol handling in QueryInteractor: update all_lines conditionally based on line_symbols presence * Add AGENTS.md for automation agent and contributor workflow guidelines * Update AGENTS.md Co-authored-by: coderabbitai[bot] <136622811+coderabbitai[bot]@users.noreply.github.com> --------- Co-authored-by: coderabbitai[bot] <136622811+coderabbitai[bot]@users.noreply.github.com> * 石橋阪大前駅ローカライズ修正 (#1336) --------- Co-authored-by: coderabbitai[bot] <136622811+coderabbitai[bot]@users.noreply.github.com> --- AGENTS.md | 65 ++++++ README.md | 4 + data/3!stations.csv | 2 +- stationapi/build.rs | 16 ++ stationapi/proto | 2 +- .../src/presentation/controller/grpc.rs | 41 +++- stationapi/src/use_case/interactor/query.rs | 209 +++++++++++++++--- stationapi/src/use_case/traits/query.rs | 8 +- 8 files changed, 306 insertions(+), 41 deletions(-) create mode 100644 AGENTS.md diff --git a/AGENTS.md b/AGENTS.md new file mode 100644 index 00000000..f09a70e4 --- /dev/null +++ b/AGENTS.md @@ -0,0 +1,65 @@ +# StationAPI Repository Guidelines + +This guide explains how automation agents and human contributors should work with the StationAPI repository so releases stay predictable, auditable, and safe. Update this file whenever you change the workflow or behavior it documents. + +## Project Layout +- `stationapi/src/domain/` – Entity definitions and repository abstractions. The `entity/` module mirrors the gRPC schema, `repository/` provides `async_trait`-based interfaces, and `normalize.rs` contains text normalization for search. +- `stationapi/src/use_case/` – Application logic. `interactor/query.rs` implements the `QueryUseCase` contract defined in `traits/query.rs`. +- `stationapi/src/infrastructure/` – SQLx repositories for PostgreSQL. `My*Repository` types share an `Arc Result<(), Box> { "StopCondition", "#[derive(serde::Serialize, serde::Deserialize)]", ) + .type_attribute( + "StationMinimal", + "#[derive(serde::Serialize, serde::Deserialize)]", + ) + .type_attribute( + "LineMinimal", + "#[derive(serde::Serialize, serde::Deserialize)]", + ) + .type_attribute( + "RouteMinimal", + "#[derive(serde::Serialize, serde::Deserialize)]", + ) + .type_attribute( + "RouteMinimalResponse", + "#[derive(serde::Serialize, serde::Deserialize)]", + ) .protoc_arg("--experimental_allow_proto3_optional") .file_descriptor_set_path(out_dir.join("stationapi_descriptor.bin")) .compile_protos(&["proto/stationapi.proto"], &["proto"])?; diff --git a/stationapi/proto b/stationapi/proto index 771b3ba7..9960d525 160000 --- a/stationapi/proto +++ b/stationapi/proto @@ -1 +1 @@ -Subproject commit 771b3ba7f68baeb0544679ca2760dc94f352f3ee +Subproject commit 9960d5259686991d2bbad39d7f3f9aaf2bbf6485 diff --git a/stationapi/src/presentation/controller/grpc.rs b/stationapi/src/presentation/controller/grpc.rs index 34ece77f..2dafd0e5 100644 --- a/stationapi/src/presentation/controller/grpc.rs +++ b/stationapi/src/presentation/controller/grpc.rs @@ -5,13 +5,13 @@ use crate::{ }, presentation::error::PresentationalError, proto::{ - station_api_server::StationApi, GetConnectedStationsRequest, GetLineByIdRequest, - GetLinesByNameRequest, GetRouteRequest, GetStationByCoordinatesRequest, + station_api_server::StationApi, GetConnectedStationsRequest, GetLineByIdListRequest, + GetLineByIdRequest, GetLinesByNameRequest, GetRouteRequest, GetStationByCoordinatesRequest, GetStationByGroupIdRequest, GetStationByIdListRequest, GetStationByIdRequest, GetStationByLineIdRequest, GetStationsByLineGroupIdRequest, GetStationsByNameRequest, GetTrainTypesByStationIdRequest, MultipleLineResponse, MultipleStationResponse, - MultipleTrainTypeResponse, Route, RouteResponse, RouteTypeResponse, SingleLineResponse, - SingleStationResponse, + MultipleTrainTypeResponse, Route, RouteMinimalResponse, RouteResponse, RouteTypeResponse, + SingleLineResponse, SingleStationResponse, }, use_case::{interactor::query::QueryInteractor, traits::query::QueryUseCase}, }; @@ -214,6 +214,21 @@ impl StationApi for MyApi { } } + async fn get_routes_minimal( + &self, + request: tonic::Request, + ) -> Result, tonic::Status> { + let from_id = request.get_ref().from_station_group_id; + let to_id = request.get_ref().to_station_group_id; + + match self.query_use_case.get_routes_minimal(from_id, to_id).await { + Ok(response) => { + return Ok(Response::new(response)); + } + Err(err) => Err(PresentationalError::from(err).into()), + } + } + async fn get_route_types( &self, request: tonic::Request, @@ -256,6 +271,24 @@ impl StationApi for MyApi { })) } + async fn get_line_by_id_list( + &self, + request: tonic::Request, + ) -> Result, tonic::Status> { + let line_ids = &request.get_ref().line_ids; + + let lines = match self.query_use_case.get_lines_by_id_vec(line_ids).await { + Ok(lines) => lines, + Err(err) => { + return Err(PresentationalError::OtherError(anyhow::anyhow!(err).into()).into()) + } + }; + + Ok(Response::new(MultipleLineResponse { + lines: lines.into_iter().map(|line| line.into()).collect(), + })) + } + async fn get_lines_by_name( &self, request: tonic::Request, diff --git a/stationapi/src/use_case/interactor/query.rs b/stationapi/src/use_case/interactor/query.rs index e9423c9a..ccf296b6 100644 --- a/stationapi/src/use_case/interactor/query.rs +++ b/stationapi/src/use_case/interactor/query.rs @@ -526,17 +526,7 @@ where .get_route_stops(from_station_id, to_station_id) .await?; - let route_row_tree_map: BTreeMap> = stops.iter().fold( - BTreeMap::new(), - |mut acc: BTreeMap>, value| { - if let Some(line_group_cd) = value.line_group_cd { - acc.entry(line_group_cd).or_default().push(value.clone()); - } else { - acc.entry(value.line_cd).or_default().push(value.clone()); - }; - acc - }, - ); + let route_row_tree_map = self.build_route_tree_map(&stops); let mut routes: Vec = Vec::new(); @@ -551,34 +541,51 @@ where .get_by_line_group_id_vec_for_routes(&line_group_id_vec) .await?; + // Add line_symbols to all lines first + for line in tt_lines.iter_mut() { + line.line_symbols = self.get_line_symbols(line); + } + let stops = stops .iter() .map(|row| { let extracted_line = self.extract_line_from_station(row); - if let Some(tt_line) = - tt_lines.iter_mut().find(|line| line.line_cd == row.line_cd) + if let Some(tt_line) = tt_lines.iter().find(|line| line.line_cd == row.line_cd) { - tt_line.line_symbols = self.get_line_symbols(tt_line); - let train_type = match row.type_id.is_some() { - true => Some(Box::new(TrainType { - id: row.type_id, - station_cd: Some(row.station_cd), - type_cd: row.type_cd, - line_group_cd: row.line_group_cd, - pass: row.pass, - type_name: row.type_name.clone().unwrap_or_default(), - type_name_k: row.type_name_k.clone().unwrap_or_default(), - type_name_r: row.type_name_r.clone(), - type_name_zh: row.type_name_zh.clone(), - type_name_ko: row.type_name_ko.clone(), - color: row.color.clone().unwrap_or_default(), - direction: row.direction, - kind: row.kind, - line: Some(Box::new(tt_line.clone())), - lines: tt_lines.to_vec(), - })), + true => { + // Filter lines to only include those with matching line_group_cd + // and remove duplicates by line_cd + let mut seen_line_cds = std::collections::HashSet::new(); + let filtered_lines: Vec = tt_lines + .iter() + .filter(|line| { + row.line_group_cd.is_some() + && line.line_group_cd == row.line_group_cd + && seen_line_cds.insert(line.line_cd) + }) + .cloned() + .collect(); + + Some(Box::new(TrainType { + id: row.type_id, + station_cd: Some(row.station_cd), + type_cd: row.type_cd, + line_group_cd: row.line_group_cd, + pass: row.pass, + type_name: row.type_name.clone().unwrap_or_default(), + type_name_k: row.type_name_k.clone().unwrap_or_default(), + type_name_r: row.type_name_r.clone(), + type_name_zh: row.type_name_zh.clone(), + type_name_ko: row.type_name_ko.clone(), + color: row.color.clone().unwrap_or_default(), + direction: row.direction, + kind: row.kind, + line: Some(Box::new(tt_line.clone())), + lines: filtered_lines, + })) + } false => None, }; @@ -609,6 +616,108 @@ where Ok(routes) } + async fn get_routes_minimal( + &self, + from_station_id: u32, + to_station_id: u32, + ) -> Result { + let stops = self + .station_repository + .get_route_stops(from_station_id, to_station_id) + .await?; + + let route_row_tree_map = self.build_route_tree_map(&stops); + + let mut routes: Vec = Vec::new(); + let mut all_lines: std::collections::HashMap = + std::collections::HashMap::new(); + + for (id, stops) in route_row_tree_map.iter() { + let stops_minimal = stops + .iter() + .map(|row| { + let extracted_line = self.extract_line_from_station(row); + + // Add line to the lines collection + let line_symbols = self + .get_line_symbols(&extracted_line) + .into_iter() + .map(|ls| proto::LineSymbol { + symbol: ls.symbol, + color: ls.color, + shape: ls.shape, + }) + .collect(); + + let line_minimal = proto::LineMinimal { + id: extracted_line.line_cd as u32, + name_short: extracted_line.line_name, + color: extracted_line.line_color_c.unwrap_or_default(), + line_type: extracted_line.line_type.unwrap_or(0), + line_symbols, + }; + + // Update line: prefer entries with non-empty line_symbols + all_lines + .entry(line_minimal.id) + .and_modify(|existing| { + // Update if new line has symbols and existing doesn't + if !line_minimal.line_symbols.is_empty() + && existing.line_symbols.is_empty() + { + *existing = line_minimal.clone(); + } + }) + .or_insert(line_minimal); + + // Create station minimal + let station_numbers = self + .get_station_numbers(row) + .into_iter() + .map(|sn| proto::StationNumber { + line_symbol: sn.line_symbol, + line_symbol_color: sn.line_symbol_color, + line_symbol_shape: sn.line_symbol_shape, + station_number: sn.station_number, + }) + .collect(); + + proto::StationMinimal { + id: row.station_cd as u32, + group_id: row.station_g_cd as u32, + name: row.station_name.clone(), + name_katakana: row.station_name_k.clone(), + name_roman: row.station_name_r.clone(), + line_ids: vec![extracted_line.line_cd as u32], + station_numbers, + stop_condition: row.pass.unwrap_or(0), + has_train_types: Some(row.type_id.is_some()), + train_type_id: row.type_id.map(|id| id as u32), + } + }) + .collect::>(); + + // TODO: SQLで同等の処理を行う + let includes_requested_station = stops_minimal + .iter() + .any(|stop| stop.group_id == from_station_id || stop.group_id == to_station_id); + if !includes_requested_station { + continue; + } + + routes.push(proto::RouteMinimal { + id: *id as u32, + stops: stops_minimal, + }); + } + + Ok(proto::RouteMinimalResponse { + routes, + lines: all_lines.into_values().collect(), + next_page_token: "".to_string(), + }) + } + async fn get_train_types( &self, from_station_id: u32, @@ -633,11 +742,16 @@ where .get_by_line_group_id_vec(&line_group_id_vec) .await?; - let tt_lines = self + let mut tt_lines = self .line_repository .get_by_line_group_id_vec(&line_group_id_vec) .await?; + // Add line_symbols to all lines first + for line in tt_lines.iter_mut() { + line.line_symbols = self.get_line_symbols(line); + } + for mut train_type in train_types.clone() { if result .iter() @@ -646,9 +760,13 @@ where continue; } + let mut seen_line_cds = HashSet::new(); train_type.lines = tt_lines .iter() - .filter(|line| line.line_group_cd == train_type.line_group_cd) + .filter(|line| { + line.line_group_cd == train_type.line_group_cd + && seen_line_cds.insert(line.line_cd) + }) .map(|line| Line { line_cd: line.line_cd, company_cd: line.company_cd, @@ -688,6 +806,10 @@ where type_cd: line.type_cd, }) .collect::>(); + + // Set the line field to the first line in the lines vector + train_type.line = train_type.lines.first().map(|l| Box::new(l.clone())); + result.push(train_type); } Ok(result) @@ -698,6 +820,11 @@ where Ok(line) } + async fn get_lines_by_id_vec(&self, line_ids: &[u32]) -> Result, UseCaseError> { + let lines = self.line_repository.get_by_ids(line_ids).await?; + Ok(lines) + } + async fn get_lines_by_name( &self, line_name: String, @@ -727,6 +854,20 @@ where TR: TrainTypeRepository, CR: CompanyRepository, { + fn build_route_tree_map(&self, stops: &[Station]) -> BTreeMap> { + stops.iter().fold( + BTreeMap::new(), + |mut acc: BTreeMap>, value| { + if let Some(line_group_cd) = value.line_group_cd { + acc.entry(line_group_cd).or_default().push(value.clone()); + } else { + acc.entry(value.line_cd).or_default().push(value.clone()); + }; + acc + }, + ) + } + fn build_station_from_row( &self, row: &Station, diff --git a/stationapi/src/use_case/traits/query.rs b/stationapi/src/use_case/traits/query.rs index 20f51457..9c5a85a6 100644 --- a/stationapi/src/use_case/traits/query.rs +++ b/stationapi/src/use_case/traits/query.rs @@ -5,7 +5,7 @@ use crate::{ company::Company, line::Line, line_symbol::LineSymbol, station::Station, station_number::StationNumber, train_type::TrainType, }, - proto::Route, + proto::{Route, RouteMinimalResponse}, use_case::error::UseCaseError, }; @@ -79,12 +79,18 @@ pub trait QueryUseCase: Send + Sync + 'static { from_station_id: u32, to_station_id: u32, ) -> Result, UseCaseError>; + async fn get_routes_minimal( + &self, + from_station_id: u32, + to_station_id: u32, + ) -> Result; async fn get_train_types( &self, from_station_id: u32, to_station_id: u32, ) -> Result, UseCaseError>; async fn find_line_by_id(&self, line_id: u32) -> Result, UseCaseError>; + async fn get_lines_by_id_vec(&self, line_ids: &[u32]) -> Result, UseCaseError>; async fn get_lines_by_name( &self, line_name: String, From b4645d3bb43050a88e005d59341a8e4e75d8ee6d Mon Sep 17 00:00:00 2001 From: Tsubasa SEKIGUCHI Date: Fri, 9 Jan 2026 08:43:25 +0900 Subject: [PATCH 2/6] master<-dev (#1374) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * SQLの致命的なバグをいくつか修正 (#1371) * ひらがな混じりで駅検索ができないバグを修正 * get_train_types_by_station_idが動かないバグを修正 * GetStationsByLineGroupIdの修正 * ユニットテスト拡充 * cargo fmt * バス路線はline_typeをOtherLineTypeに強制 (#1372) * direction_idがNULLのトリップに存在するバス停も正しく取り込むよう修正 (#1373) * direction_idがNULLのトリップに存在するバス停も正しく取り込むよう修正 GTFSの仕様ではdirection_idはオプショナルであり、NULLの場合でも バス停は有効な停留所として扱うべきである。 これまではvariant_only_with_neighbors CTEで「direction_id IS NOT NULL」 のトリップにのみ存在するバス停をフィルタリングしていたため、 早81の原宿駅前や渋谷駅東口などのバス停がレスポンスから除外されていた。 * バリアントバス停の位置推定でメイントリップに存在する隣接バス停を優先 variant_only_with_neighborsでバス停の隣接情報を選択する際に、 隣接バス停(prev_stop_id/next_stop_id)がメイントリップに存在する レコードを優先するよう修正。 これにより、原宿駅前などのバス停がより正確な位置に挿入される。 --------- Co-authored-by: Claude --------- Co-authored-by: Claude --- ...35b00edeb2b60996e3e7333772ed9202f327.json} | 98 +++--- ...05ca06ee06027d6a0e4bd07cf73e8554226b.json} | 4 +- stationapi/src/domain/normalize.rs | 69 ++++ stationapi/src/import.rs | 26 +- .../src/infrastructure/line_repository.rs | 149 +++++++- .../src/infrastructure/station_repository.rs | 155 +++++++- stationapi/src/use_case/dto/line.rs | 332 +++++++++++++++++- stationapi/src/use_case/interactor/query.rs | 2 +- 8 files changed, 749 insertions(+), 86 deletions(-) rename .sqlx/{query-d3f9188af86adfa1b16ea4ce538299c4e0942aeadc2497c7752b6b98fb78087f.json => query-25923b43426ac7b1829aaf1c726635b00edeb2b60996e3e7333772ed9202f327.json} (61%) rename .sqlx/{query-6a624360045e2278d6c1e08559918dcd026348e9304bba9584e72061034ac3db.json => query-269872b7f724f7361c016b696ddc05ca06ee06027d6a0e4bd07cf73e8554226b.json} (94%) diff --git a/.sqlx/query-d3f9188af86adfa1b16ea4ce538299c4e0942aeadc2497c7752b6b98fb78087f.json b/.sqlx/query-25923b43426ac7b1829aaf1c726635b00edeb2b60996e3e7333772ed9202f327.json similarity index 61% rename from .sqlx/query-d3f9188af86adfa1b16ea4ce538299c4e0942aeadc2497c7752b6b98fb78087f.json rename to .sqlx/query-25923b43426ac7b1829aaf1c726635b00edeb2b60996e3e7333772ed9202f327.json index 54d2fc59..f4914ee2 100644 --- a/.sqlx/query-d3f9188af86adfa1b16ea4ce538299c4e0942aeadc2497c7752b6b98fb78087f.json +++ b/.sqlx/query-25923b43426ac7b1829aaf1c726635b00edeb2b60996e3e7333772ed9202f327.json @@ -1,6 +1,6 @@ { "db_name": "PostgreSQL", - "query": "SELECT l.line_cd,\n l.company_cd,\n l.line_type,\n l.line_symbol1,\n l.line_symbol2,\n l.line_symbol3,\n l.line_symbol4,\n l.line_symbol1_color,\n l.line_symbol2_color,\n l.line_symbol3_color,\n l.line_symbol4_color,\n l.line_symbol1_shape,\n l.line_symbol2_shape,\n l.line_symbol3_shape,\n l.line_symbol4_shape,\n l.e_status,\n l.e_sort,\n COALESCE(l.average_distance, 0.0)::DOUBLE PRECISION AS average_distance,\n s.station_cd,\n s.station_g_cd,\n sst.line_group_cd,\n sst.type_cd,\n COALESCE(alias_data.line_name, l.line_name) AS line_name,\n COALESCE(alias_data.line_name_k, l.line_name_k) AS line_name_k,\n COALESCE(alias_data.line_name_h, l.line_name_h) AS line_name_h,\n COALESCE(alias_data.line_name_r, l.line_name_r) AS line_name_r,\n COALESCE(alias_data.line_name_zh, l.line_name_zh) AS line_name_zh,\n COALESCE(alias_data.line_name_ko, l.line_name_ko) AS line_name_ko,\n COALESCE(alias_data.line_color_c, l.line_color_c) AS line_color_c,\n l.transport_type\n FROM lines AS l\n JOIN stations AS s ON s.station_cd = $1\n LEFT JOIN station_station_types AS sst ON sst.station_cd = s.station_cd AND sst.pass <> 1\n LEFT JOIN (\n SELECT DISTINCT ON (la.station_cd)\n la.station_cd,\n a.line_name,\n a.line_name_k,\n a.line_name_h,\n a.line_name_r,\n a.line_name_zh,\n a.line_name_ko,\n a.line_color_c\n FROM line_aliases AS la\n JOIN aliases AS a ON la.alias_cd = a.id\n WHERE la.station_cd = $1\n LIMIT 1\n ) AS alias_data ON alias_data.station_cd = s.station_cd\n WHERE l.line_cd = s.line_cd", + "query": "SELECT l.line_cd,\n l.company_cd,\n l.line_type,\n COALESCE(alias_data.line_name, l.line_name) AS line_name,\n COALESCE(alias_data.line_name_k, l.line_name_k) AS line_name_k,\n COALESCE(alias_data.line_name_h, l.line_name_h) AS line_name_h,\n COALESCE(alias_data.line_name_r, l.line_name_r) AS line_name_r,\n COALESCE(alias_data.line_name_zh, l.line_name_zh) AS line_name_zh,\n COALESCE(alias_data.line_name_ko, l.line_name_ko) AS line_name_ko,\n COALESCE(alias_data.line_color_c, l.line_color_c) AS line_color_c,\n l.line_symbol1,\n l.line_symbol2,\n l.line_symbol3,\n l.line_symbol4,\n l.line_symbol1_color,\n l.line_symbol2_color,\n l.line_symbol3_color,\n l.line_symbol4_color,\n l.line_symbol1_shape,\n l.line_symbol2_shape,\n l.line_symbol3_shape,\n l.line_symbol4_shape,\n l.e_status,\n l.e_sort,\n COALESCE(l.average_distance, 0.0)::DOUBLE PRECISION AS average_distance,\n sst.line_group_cd AS \"line_group_cd?\",\n s.station_cd,\n s.station_g_cd,\n sst.type_cd AS \"type_cd?\",\n l.transport_type\n FROM lines AS l\n JOIN stations AS s ON s.station_cd = $1\n LEFT JOIN station_station_types AS sst ON sst.station_cd = s.station_cd AND sst.pass <> 1\n LEFT JOIN (\n SELECT DISTINCT ON (la.station_cd)\n la.station_cd,\n a.line_name,\n a.line_name_k,\n a.line_name_h,\n a.line_name_r,\n a.line_name_zh,\n a.line_name_ko,\n a.line_color_c\n FROM line_aliases AS la\n JOIN aliases AS a ON la.alias_cd = a.id\n WHERE la.station_cd = $1\n LIMIT 1\n ) AS alias_data ON alias_data.station_cd = s.station_cd\n WHERE l.line_cd = s.line_cd", "describe": { "columns": [ { @@ -20,133 +20,133 @@ }, { "ordinal": 3, - "name": "line_symbol1", + "name": "line_name", "type_info": "Text" }, { "ordinal": 4, - "name": "line_symbol2", + "name": "line_name_k", "type_info": "Text" }, { "ordinal": 5, - "name": "line_symbol3", + "name": "line_name_h", "type_info": "Text" }, { "ordinal": 6, - "name": "line_symbol4", + "name": "line_name_r", "type_info": "Text" }, { "ordinal": 7, - "name": "line_symbol1_color", + "name": "line_name_zh", "type_info": "Text" }, { "ordinal": 8, - "name": "line_symbol2_color", + "name": "line_name_ko", "type_info": "Text" }, { "ordinal": 9, - "name": "line_symbol3_color", + "name": "line_color_c", "type_info": "Text" }, { "ordinal": 10, - "name": "line_symbol4_color", + "name": "line_symbol1", "type_info": "Text" }, { "ordinal": 11, - "name": "line_symbol1_shape", + "name": "line_symbol2", "type_info": "Text" }, { "ordinal": 12, - "name": "line_symbol2_shape", + "name": "line_symbol3", "type_info": "Text" }, { "ordinal": 13, - "name": "line_symbol3_shape", + "name": "line_symbol4", "type_info": "Text" }, { "ordinal": 14, - "name": "line_symbol4_shape", + "name": "line_symbol1_color", "type_info": "Text" }, { "ordinal": 15, - "name": "e_status", - "type_info": "Int4" + "name": "line_symbol2_color", + "type_info": "Text" }, { "ordinal": 16, - "name": "e_sort", - "type_info": "Int4" + "name": "line_symbol3_color", + "type_info": "Text" }, { "ordinal": 17, - "name": "average_distance", - "type_info": "Float8" + "name": "line_symbol4_color", + "type_info": "Text" }, { "ordinal": 18, - "name": "station_cd", - "type_info": "Int4" + "name": "line_symbol1_shape", + "type_info": "Text" }, { "ordinal": 19, - "name": "station_g_cd", - "type_info": "Int4" + "name": "line_symbol2_shape", + "type_info": "Text" }, { "ordinal": 20, - "name": "line_group_cd", - "type_info": "Int4" + "name": "line_symbol3_shape", + "type_info": "Text" }, { "ordinal": 21, - "name": "type_cd", - "type_info": "Int4" + "name": "line_symbol4_shape", + "type_info": "Text" }, { "ordinal": 22, - "name": "line_name", - "type_info": "Text" + "name": "e_status", + "type_info": "Int4" }, { "ordinal": 23, - "name": "line_name_k", - "type_info": "Text" + "name": "e_sort", + "type_info": "Int4" }, { "ordinal": 24, - "name": "line_name_h", - "type_info": "Text" + "name": "average_distance", + "type_info": "Float8" }, { "ordinal": 25, - "name": "line_name_r", - "type_info": "Text" + "name": "line_group_cd?", + "type_info": "Int4" }, { "ordinal": 26, - "name": "line_name_zh", - "type_info": "Text" + "name": "station_cd", + "type_info": "Int4" }, { "ordinal": 27, - "name": "line_name_ko", - "type_info": "Text" + "name": "station_g_cd", + "type_info": "Int4" }, { "ordinal": 28, - "name": "line_color_c", - "type_info": "Text" + "name": "type_cd?", + "type_info": "Int4" }, { "ordinal": 29, @@ -163,6 +163,13 @@ false, false, false, + null, + null, + null, + null, + null, + null, + null, true, true, true, @@ -182,15 +189,8 @@ false, false, false, - null, - null, - null, - null, - null, - null, - null, false ] }, - "hash": "d3f9188af86adfa1b16ea4ce538299c4e0942aeadc2497c7752b6b98fb78087f" + "hash": "25923b43426ac7b1829aaf1c726635b00edeb2b60996e3e7333772ed9202f327" } diff --git a/.sqlx/query-6a624360045e2278d6c1e08559918dcd026348e9304bba9584e72061034ac3db.json b/.sqlx/query-269872b7f724f7361c016b696ddc05ca06ee06027d6a0e4bd07cf73e8554226b.json similarity index 94% rename from .sqlx/query-6a624360045e2278d6c1e08559918dcd026348e9304bba9584e72061034ac3db.json rename to .sqlx/query-269872b7f724f7361c016b696ddc05ca06ee06027d6a0e4bd07cf73e8554226b.json index 7f837cee..b44f8430 100644 --- a/.sqlx/query-6a624360045e2278d6c1e08559918dcd026348e9304bba9584e72061034ac3db.json +++ b/.sqlx/query-269872b7f724f7361c016b696ddc05ca06ee06027d6a0e4bd07cf73e8554226b.json @@ -1,6 +1,6 @@ { "db_name": "PostgreSQL", - "query": "SELECT\n s.station_cd,\n s.station_g_cd,\n s.station_name,\n s.station_name_k,\n s.station_name_r,\n s.station_name_rn,\n s.station_name_zh,\n s.station_name_ko,\n s.station_number1,\n s.station_number2,\n s.station_number3,\n s.station_number4,\n s.three_letter_code,\n s.line_cd,\n s.pref_cd,\n s.post,\n s.address,\n s.lon,\n s.lat,\n s.open_ymd,\n s.close_ymd,\n s.e_status,\n s.e_sort,\n COALESCE(NULLIF(COALESCE(a.line_name, l.line_name), ''), NULL) AS line_name,\n COALESCE(NULLIF(COALESCE(a.line_name_k, l.line_name_k), ''), NULL) AS line_name_k,\n COALESCE(NULLIF(COALESCE(a.line_name_h, l.line_name_h), ''), NULL) AS line_name_h,\n COALESCE(NULLIF(COALESCE(a.line_name_r, l.line_name_r), ''), NULL) AS line_name_r,\n COALESCE(NULLIF(COALESCE(a.line_name_zh, l.line_name_zh), ''), NULL) AS line_name_zh,\n COALESCE(NULLIF(COALESCE(a.line_name_ko, l.line_name_ko), ''), NULL) AS line_name_ko,\n COALESCE(NULLIF(COALESCE(a.line_color_c, l.line_color_c), ''), NULL) AS line_color_c,\n l.company_cd,\n l.line_type,\n l.line_symbol1,\n l.line_symbol2,\n l.line_symbol3,\n l.line_symbol4,\n l.line_symbol1_color,\n l.line_symbol2_color,\n l.line_symbol3_color,\n l.line_symbol4_color,\n l.line_symbol1_shape,\n l.line_symbol2_shape,\n l.line_symbol3_shape,\n l.line_symbol4_shape,\n COALESCE(l.average_distance, 0.0)::DOUBLE PRECISION AS average_distance,\n sst.id AS sst_id,\n sst.type_cd,\n sst.line_group_cd,\n sst.pass,\n t.id AS type_id,\n t.type_name,\n t.type_name_k,\n t.type_name_r,\n t.type_name_zh,\n t.type_name_ko,\n t.color,\n t.direction,\n t.kind,\n s.transport_type\n FROM stations AS s\n JOIN lines AS l ON l.line_cd = s.line_cd AND l.e_status = 0\n LEFT JOIN station_station_types AS sst ON sst.line_group_cd = $1\n LEFT JOIN types AS t ON t.type_cd = sst.type_cd\n LEFT JOIN line_aliases AS la ON la.station_cd = s.station_cd\n LEFT JOIN aliases AS a ON a.id = la.alias_cd\n WHERE\n s.line_cd = l.line_cd\n AND s.station_cd = sst.station_cd\n AND s.e_status = 0\n ORDER BY sst.id", + "query": "SELECT\n s.station_cd,\n s.station_g_cd,\n s.station_name,\n s.station_name_k,\n s.station_name_r,\n s.station_name_rn,\n s.station_name_zh,\n s.station_name_ko,\n s.station_number1,\n s.station_number2,\n s.station_number3,\n s.station_number4,\n s.three_letter_code,\n s.line_cd,\n s.pref_cd,\n s.post,\n s.address,\n s.lon,\n s.lat,\n s.open_ymd,\n s.close_ymd,\n s.e_status,\n s.e_sort,\n COALESCE(NULLIF(COALESCE(a.line_name, l.line_name), ''), NULL) AS line_name,\n COALESCE(NULLIF(COALESCE(a.line_name_k, l.line_name_k), ''), NULL) AS line_name_k,\n COALESCE(NULLIF(COALESCE(a.line_name_h, l.line_name_h), ''), NULL) AS line_name_h,\n COALESCE(NULLIF(COALESCE(a.line_name_r, l.line_name_r), ''), NULL) AS line_name_r,\n COALESCE(NULLIF(COALESCE(a.line_name_zh, l.line_name_zh), ''), NULL) AS line_name_zh,\n COALESCE(NULLIF(COALESCE(a.line_name_ko, l.line_name_ko), ''), NULL) AS line_name_ko,\n COALESCE(NULLIF(COALESCE(a.line_color_c, l.line_color_c), ''), NULL) AS line_color_c,\n l.company_cd,\n l.line_type,\n l.line_symbol1,\n l.line_symbol2,\n l.line_symbol3,\n l.line_symbol4,\n l.line_symbol1_color,\n l.line_symbol2_color,\n l.line_symbol3_color,\n l.line_symbol4_color,\n l.line_symbol1_shape,\n l.line_symbol2_shape,\n l.line_symbol3_shape,\n l.line_symbol4_shape,\n COALESCE(l.average_distance, 0.0)::DOUBLE PRECISION AS average_distance,\n sst.id AS sst_id,\n sst.type_cd,\n sst.line_group_cd,\n sst.pass,\n t.id AS type_id,\n t.type_name,\n t.type_name_k,\n t.type_name_r,\n t.type_name_zh,\n t.type_name_ko,\n t.color,\n t.direction,\n t.kind,\n s.transport_type\n FROM stations AS s\n JOIN lines AS l ON l.line_cd = s.line_cd AND l.e_status = 0\n JOIN station_station_types AS sst ON sst.line_group_cd = $1 AND sst.station_cd = s.station_cd\n JOIN types AS t ON t.type_cd = sst.type_cd\n LEFT JOIN line_aliases AS la ON la.station_cd = s.station_cd\n LEFT JOIN aliases AS a ON a.id = la.alias_cd\n WHERE\n s.e_status = 0\n ORDER BY sst.id", "describe": { "columns": [ { @@ -366,5 +366,5 @@ false ] }, - "hash": "6a624360045e2278d6c1e08559918dcd026348e9304bba9584e72061034ac3db" + "hash": "269872b7f724f7361c016b696ddc05ca06ee06027d6a0e4bd07cf73e8554226b" } diff --git a/stationapi/src/domain/normalize.rs b/stationapi/src/domain/normalize.rs index 9eb6c831..c9c91c98 100644 --- a/stationapi/src/domain/normalize.rs +++ b/stationapi/src/domain/normalize.rs @@ -33,4 +33,73 @@ mod tests { // ひらがな・カタカナ混合のテスト assert_eq!(normalize_for_search("とうキョウ"), "トウキョウ"); } + + #[test] + fn test_normalize_hiragana_to_katakana() { + // 全ひらがな→カタカナ変換 + assert_eq!(normalize_for_search("しんじゅく"), "シンジュク"); + assert_eq!(normalize_for_search("おおさか"), "オオサカ"); + assert_eq!(normalize_for_search("きょうと"), "キョウト"); + // 小さいひらがな + assert_eq!(normalize_for_search("ぁぃぅぇぉ"), "ァィゥェォ"); + assert_eq!(normalize_for_search("っゃゅょ"), "ッャュョ"); + // ひらがなの範囲の端(ぁ〜ん) + assert_eq!(normalize_for_search("ぁ"), "ァ"); + assert_eq!(normalize_for_search("ん"), "ン"); + } + + #[test] + fn test_normalize_fullwidth_numbers() { + // 全角数字→半角数字変換 + assert_eq!(normalize_for_search("0123456789"), "0123456789"); + assert_eq!(normalize_for_search("東京123"), "東京123"); + } + + #[test] + fn test_normalize_fullwidth_alphabet() { + // 全角英字→半角英字変換 + assert_eq!(normalize_for_search("ABCDE"), "ABCDE"); + assert_eq!(normalize_for_search("abcde"), "abcde"); + assert_eq!(normalize_for_search("Z"), "Z"); + assert_eq!(normalize_for_search("z"), "z"); + } + + #[test] + fn test_normalize_mixed_input() { + // 漢字+ひらがな+全角数字の混合 + assert_eq!(normalize_for_search("東京駅1番線"), "東京駅1番線"); + // ひらがな+漢字+全角英字 + assert_eq!( + normalize_for_search("しんじゅく駅WEST"), + "シンジュク駅WEST" + ); + // 複合パターン + assert_eq!( + normalize_for_search("とうきょう123ABC"), + "トウキョウ123ABC" + ); + } + + #[test] + fn test_normalize_preserves_other_characters() { + // カタカナはそのまま + assert_eq!(normalize_for_search("トウキョウ"), "トウキョウ"); + // 漢字はそのまま + assert_eq!(normalize_for_search("東京"), "東京"); + // 半角英数字はそのまま + assert_eq!(normalize_for_search("Tokyo123"), "Tokyo123"); + // 記号はそのまま + assert_eq!(normalize_for_search("東京-品川"), "東京-品川"); + assert_eq!(normalize_for_search("(テスト)"), "(テスト)"); + } + + #[test] + fn test_normalize_station_name_search_patterns() { + // 実際の駅名検索で使われるパターン + assert_eq!(normalize_for_search("しながわ"), "シナガワ"); + assert_eq!(normalize_for_search("うえの"), "ウエノ"); + assert_eq!(normalize_for_search("あきはばら"), "アキハバラ"); + assert_eq!(normalize_for_search("いけぶくろ"), "イケブクロ"); + assert_eq!(normalize_for_search("しぶや"), "シブヤ"); + } } diff --git a/stationapi/src/import.rs b/stationapi/src/import.rs index 6f4f2e77..1b991878 100644 --- a/stationapi/src/import.rs +++ b/stationapi/src/import.rs @@ -1636,7 +1636,7 @@ async fn build_stop_route_mapping( ) ), -- Find variant-only stops (not on main trip) with their neighbor info - -- Exclude stops that ONLY appear on NULL direction_id trips (loop routes) + -- Prioritize records where neighbors exist on main trip for better position estimation variant_only_with_neighbors AS ( SELECT DISTINCT ON (vts.parent_stop_id, vts.route_id) vts.parent_stop_id, @@ -1645,21 +1645,25 @@ async fn build_stop_route_mapping( vts.prev_stop_id, vts.next_stop_id FROM variant_trip_stops_with_neighbors vts + LEFT JOIN main_trip_stops mts_prev + ON vts.prev_stop_id = mts_prev.parent_stop_id + AND vts.route_id = mts_prev.route_id + LEFT JOIN main_trip_stops mts_next + ON vts.next_stop_id = mts_next.parent_stop_id + AND vts.route_id = mts_next.route_id WHERE NOT EXISTS ( SELECT 1 FROM main_trip_stops mts WHERE mts.parent_stop_id = vts.parent_stop_id AND mts.route_id = vts.route_id ) - -- Only include stops that appear on at least one non-NULL direction_id trip - AND EXISTS ( - SELECT 1 FROM gtfs_trips gt2 - JOIN gtfs_stop_times gst2 ON gt2.trip_id = gst2.trip_id - JOIN gtfs_stops gs2 ON gst2.stop_id = gs2.stop_id - WHERE gt2.route_id = vts.route_id - AND COALESCE(gs2.parent_station, gs2.stop_id) = vts.parent_stop_id - AND gt2.direction_id IS NOT NULL - ) - ORDER BY vts.parent_stop_id, vts.route_id, vts.stop_sequence + ORDER BY vts.parent_stop_id, vts.route_id, + -- Prioritize records where neighbors exist on main trip + CASE + WHEN mts_prev.parent_stop_id IS NOT NULL AND mts_next.parent_stop_id IS NOT NULL THEN 0 + WHEN mts_prev.parent_stop_id IS NOT NULL OR mts_next.parent_stop_id IS NOT NULL THEN 1 + ELSE 2 + END, + vts.stop_sequence ), -- Recursive CTE to find the nearest main-trip stop by following prev chain prev_chain AS ( diff --git a/stationapi/src/infrastructure/line_repository.rs b/stationapi/src/infrastructure/line_repository.rs index b90dcc97..725eb42d 100644 --- a/stationapi/src/infrastructure/line_repository.rs +++ b/stationapi/src/infrastructure/line_repository.rs @@ -218,9 +218,16 @@ impl InternalLineRepository { let station_id = station_id as i32; let rows: Option = sqlx::query_as!( LineRow, - "SELECT l.line_cd, + r#"SELECT l.line_cd, l.company_cd, l.line_type, + COALESCE(alias_data.line_name, l.line_name) AS line_name, + COALESCE(alias_data.line_name_k, l.line_name_k) AS line_name_k, + COALESCE(alias_data.line_name_h, l.line_name_h) AS line_name_h, + COALESCE(alias_data.line_name_r, l.line_name_r) AS line_name_r, + COALESCE(alias_data.line_name_zh, l.line_name_zh) AS line_name_zh, + COALESCE(alias_data.line_name_ko, l.line_name_ko) AS line_name_ko, + COALESCE(alias_data.line_color_c, l.line_color_c) AS line_color_c, l.line_symbol1, l.line_symbol2, l.line_symbol3, @@ -236,17 +243,10 @@ impl InternalLineRepository { l.e_status, l.e_sort, COALESCE(l.average_distance, 0.0)::DOUBLE PRECISION AS average_distance, + sst.line_group_cd AS "line_group_cd?", s.station_cd, s.station_g_cd, - sst.line_group_cd, - sst.type_cd, - COALESCE(alias_data.line_name, l.line_name) AS line_name, - COALESCE(alias_data.line_name_k, l.line_name_k) AS line_name_k, - COALESCE(alias_data.line_name_h, l.line_name_h) AS line_name_h, - COALESCE(alias_data.line_name_r, l.line_name_r) AS line_name_r, - COALESCE(alias_data.line_name_zh, l.line_name_zh) AS line_name_zh, - COALESCE(alias_data.line_name_ko, l.line_name_ko) AS line_name_ko, - COALESCE(alias_data.line_color_c, l.line_color_c) AS line_color_c, + sst.type_cd AS "type_cd?", l.transport_type FROM lines AS l JOIN stations AS s ON s.station_cd = $1 @@ -266,7 +266,7 @@ impl InternalLineRepository { WHERE la.station_cd = $1 LIMIT 1 ) AS alias_data ON alias_data.station_cd = s.station_cd - WHERE l.line_cd = s.line_cd", + WHERE l.line_cd = s.line_cd"#, station_id, ) .fetch_optional(conn) @@ -1522,4 +1522,131 @@ mod tests { cleanup_test_data(&repository.pool).await; } + + // ============================================ + // Tests for find_by_station_id optional fields + // (line_group_cd and type_cd are now optional) + // ============================================ + + #[tokio::test] + async fn test_line_row_with_optional_line_group_cd_none() { + // line_group_cdがNoneの場合(station_station_typesにマッチしない駅) + let line_row = LineRow { + line_cd: 1, + company_cd: 1, + line_type: Some(1), + line_name: Some("Test Line".to_string()), + line_name_k: Some("テストライン".to_string()), + line_name_h: Some("テストライン".to_string()), + line_name_r: Some("Test Line".to_string()), + line_name_zh: None, + line_name_ko: None, + line_color_c: Some("#FF0000".to_string()), + line_symbol1: None, + line_symbol2: None, + line_symbol3: None, + line_symbol4: None, + line_symbol1_color: None, + line_symbol2_color: None, + line_symbol3_color: None, + line_symbol4_color: None, + line_symbol1_shape: None, + line_symbol2_shape: None, + line_symbol3_shape: None, + line_symbol4_shape: None, + e_status: 0, + e_sort: 1, + average_distance: Some(1.5), + line_group_cd: None, // 重要: オプショナルでNone + station_cd: Some(101), + station_g_cd: Some(201), + type_cd: None, // 重要: オプショナルでNone + transport_type: Some(0), + }; + + let line: Line = line_row.into(); + + assert_eq!(line.line_cd, 1); + assert_eq!(line.station_cd, Some(101)); + assert_eq!(line.station_g_cd, Some(201)); + assert_eq!(line.line_group_cd, None); + assert_eq!(line.type_cd, None); + } + + #[tokio::test] + async fn test_line_row_with_optional_line_group_cd_some() { + // line_group_cdがSomeの場合(station_station_typesにマッチする駅) + let line_row = LineRow { + line_cd: 1, + company_cd: 1, + line_type: Some(1), + line_name: Some("Test Line".to_string()), + line_name_k: Some("テストライン".to_string()), + line_name_h: Some("テストライン".to_string()), + line_name_r: Some("Test Line".to_string()), + line_name_zh: None, + line_name_ko: None, + line_color_c: Some("#FF0000".to_string()), + line_symbol1: None, + line_symbol2: None, + line_symbol3: None, + line_symbol4: None, + line_symbol1_color: None, + line_symbol2_color: None, + line_symbol3_color: None, + line_symbol4_color: None, + line_symbol1_shape: None, + line_symbol2_shape: None, + line_symbol3_shape: None, + line_symbol4_shape: None, + e_status: 0, + e_sort: 1, + average_distance: Some(1.5), + line_group_cd: Some(301), // 重要: オプショナルでSome + station_cd: Some(101), + station_g_cd: Some(201), + type_cd: Some(1), // 重要: オプショナルでSome + transport_type: Some(0), + }; + + let line: Line = line_row.into(); + + assert_eq!(line.line_cd, 1); + assert_eq!(line.station_cd, Some(101)); + assert_eq!(line.station_g_cd, Some(201)); + assert_eq!(line.line_group_cd, Some(301)); + assert_eq!(line.type_cd, Some(1)); + } + + #[tokio::test] + #[cfg_attr(not(feature = "integration-tests"), ignore)] + async fn test_find_by_station_id_without_station_station_types() { + // station_station_typesにエントリがない駅でもfind_by_station_idが動作することを確認 + let pool = setup_test_db().await; + + // テストデータをセットアップ(station_station_typesにエントリがない駅を追加) + setup_test_data(&pool).await; + + // station_station_typesにエントリがない駅を追加 + sqlx::query( + "INSERT INTO stations (station_cd, station_g_cd, line_cd, e_status) VALUES (200, 300, 1, 0)" + ) + .execute(&pool) + .await + .unwrap(); + + let mut conn = pool.acquire().await.unwrap(); + let result = InternalLineRepository::find_by_station_id(200, &mut conn).await; + + assert!(result.is_ok()); + let line = result.unwrap(); + // LEFT JOINなので駅は見つかるが、line_group_cdとtype_cdはNone + if let Some(line) = line { + assert_eq!(line.station_cd, Some(200)); + assert_eq!(line.line_group_cd, None); + assert_eq!(line.type_cd, None); + } + + cleanup_test_data(&pool).await; + } } diff --git a/stationapi/src/infrastructure/station_repository.rs b/stationapi/src/infrastructure/station_repository.rs index 621fff40..14c4c385 100644 --- a/stationapi/src/infrastructure/station_repository.rs +++ b/stationapi/src/infrastructure/station_repository.rs @@ -6,6 +6,7 @@ use crate::{ domain::{ entity::{gtfs::TransportType, station::Station}, error::DomainError, + normalize::normalize_for_search, repository::station_repository::StationRepository, }, proto::StopCondition, @@ -1005,7 +1006,10 @@ impl InternalStationRepository { transport_type: Option, conn: &mut PgConnection, ) -> Result, DomainError> { - let station_name = &(format!("%{station_name}%")); + // 元の入力用パターン(漢字・その他) + let station_name_pattern = &(format!("%{station_name}%")); + // カタカナ検索用に正規化されたパターン(ひらがな→カタカナ変換) + let station_name_k_pattern = &(format!("%{}%", normalize_for_search(&station_name))); let limit = limit.map(|v| v as i64); let from_station_group_id = from_station_group_id.map(|id| id as i32); let transport_type_value: Option = transport_type.map(|t| t as i32); @@ -1127,11 +1131,11 @@ impl InternalStationRepository { ORDER BY station_g_cd, station_name LIMIT $7"#, from_station_group_id, - station_name, - station_name, - station_name, - station_name, - station_name, + station_name_pattern, + station_name_pattern, + station_name_k_pattern, // station_name_k用には正規化されたパターンを使用 + station_name_pattern, + station_name_pattern, limit, transport_type_value ) @@ -1211,14 +1215,12 @@ impl InternalStationRepository { s.transport_type FROM stations AS s JOIN lines AS l ON l.line_cd = s.line_cd AND l.e_status = 0 - LEFT JOIN station_station_types AS sst ON sst.line_group_cd = $1 - LEFT JOIN types AS t ON t.type_cd = sst.type_cd + JOIN station_station_types AS sst ON sst.line_group_cd = $1 AND sst.station_cd = s.station_cd + JOIN types AS t ON t.type_cd = sst.type_cd LEFT JOIN line_aliases AS la ON la.station_cd = s.station_cd LEFT JOIN aliases AS a ON a.id = la.alias_cd WHERE - s.line_cd = l.line_cd - AND s.station_cd = sst.station_cd - AND s.e_status = 0 + s.e_status = 0 ORDER BY sst.id"#, line_group_id as i32 ) @@ -1716,4 +1718,135 @@ mod tests { // This test would require proper database setup and test data // Skipped in regular test runs } + + // ============================================ + // Tests for get_by_name search pattern generation + // ============================================ + + mod get_by_name_tests { + use crate::domain::normalize::normalize_for_search; + + #[test] + fn test_search_pattern_generation() { + // station_name_pattern: 元の入力をそのまま使用 + let station_name = "しんじゅく"; + let station_name_pattern = format!("%{station_name}%"); + assert_eq!(station_name_pattern, "%しんじゅく%"); + + // station_name_k_pattern: ひらがな→カタカナ変換して使用 + let station_name_k_pattern = format!("%{}%", normalize_for_search(station_name)); + assert_eq!(station_name_k_pattern, "%シンジュク%"); + } + + #[test] + fn test_hiragana_search_converts_to_katakana_for_name_k() { + // ひらがな入力の場合、station_name_kはカタカナに変換される + let input = "とうきょう"; + let pattern_for_name_k = format!("%{}%", normalize_for_search(input)); + assert_eq!(pattern_for_name_k, "%トウキョウ%"); + } + + #[test] + fn test_katakana_search_remains_katakana() { + // カタカナ入力の場合、station_name_kもカタカナのまま + let input = "トウキョウ"; + let pattern_for_name_k = format!("%{}%", normalize_for_search(input)); + assert_eq!(pattern_for_name_k, "%トウキョウ%"); + } + + #[test] + fn test_kanji_search_remains_kanji_for_name() { + // 漢字入力の場合、station_nameは漢字のまま + let input = "東京"; + let pattern_for_name = format!("%{input}%"); + assert_eq!(pattern_for_name, "%東京%"); + + // station_name_kも漢字のまま(normalize_for_searchは漢字を変換しない) + let pattern_for_name_k = format!("%{}%", normalize_for_search(input)); + assert_eq!(pattern_for_name_k, "%東京%"); + } + + #[test] + fn test_mixed_hiragana_kanji_search() { + // ひらがな+漢字の混合入力 + let input = "しん宿"; + let pattern_for_name = format!("%{input}%"); + assert_eq!(pattern_for_name, "%しん宿%"); + + // station_name_k用: ひらがな部分だけカタカナに変換 + let pattern_for_name_k = format!("%{}%", normalize_for_search(input)); + assert_eq!(pattern_for_name_k, "%シン宿%"); + } + + #[test] + fn test_search_pattern_with_special_stations() { + // 実際の駅名での動作確認 + let test_cases = vec![ + ("しながわ", "%シナガワ%"), + ("うえの", "%ウエノ%"), + ("あきはばら", "%アキハバラ%"), + ("いけぶくろ", "%イケブクロ%"), + ("おおさか", "%オオサカ%"), + ]; + + for (input, expected_k_pattern) in test_cases { + let pattern_for_name_k = format!("%{}%", normalize_for_search(input)); + assert_eq!( + pattern_for_name_k, expected_k_pattern, + "Failed for input: {input}" + ); + } + } + } + + // ============================================ + // Tests for get_by_line_group_id SQL structure + // ============================================ + + mod get_by_line_group_id_tests { + #[test] + fn test_line_group_id_join_condition_structure() { + // JOINの条件が正しく構成されることを確認 + // 修正後: JOIN station_station_types AS sst ON sst.line_group_cd = $1 AND sst.station_cd = s.station_cd + let line_group_id = 1; + let join_condition = format!( + "sst.line_group_cd = {} AND sst.station_cd = s.station_cd", + line_group_id + ); + assert!(join_condition.contains("sst.line_group_cd = 1")); + assert!(join_condition.contains("sst.station_cd = s.station_cd")); + } + + #[test] + fn test_line_group_id_parameter_binding() { + // パラメータが正しくバインドされることを確認 + let line_group_id: u32 = 123; + let bound_value = line_group_id as i32; + assert_eq!(bound_value, 123); + } + } + + // ============================================ + // Database integration tests (require actual DB) + // ============================================ + + #[tokio::test] + #[ignore] // Requires actual database setup + async fn test_get_by_name_with_hiragana_search() { + // ひらがなで駅を検索できることを確認 + // 例: "しんじゅく" で "新宿" (station_name_k: "シンジュク") がヒットする + } + + #[tokio::test] + #[ignore] // Requires actual database setup + async fn test_get_by_line_group_id_returns_correct_stations() { + // line_group_idで指定した路線グループの駅のみが返されることを確認 + // 以前のバグ: JOINの条件が不完全で意図しない駅が返されていた + } + + #[tokio::test] + #[ignore] // Requires actual database setup + async fn test_get_by_line_group_id_station_order() { + // 返される駅がsst.idの順序でソートされていることを確認 + } } diff --git a/stationapi/src/use_case/dto/line.rs b/stationapi/src/use_case/dto/line.rs index 16cbea73..9f18aeee 100644 --- a/stationapi/src/use_case/dto/line.rs +++ b/stationapi/src/use_case/dto/line.rs @@ -5,6 +5,14 @@ use crate::{ impl From for GrpcLine { fn from(line: Line) -> Self { + // バス路線の場合は line_type を OtherLineType (0) に強制 + // (鉄道用の line_type が誤って設定されている可能性があるため) + let line_type = if line.transport_type == TransportType::Bus { + 0 // OtherLineType + } else { + line.line_type.unwrap_or_default() + }; + Self { id: line.line_cd as u32, name_short: line.line_name, @@ -14,7 +22,7 @@ impl From for GrpcLine { name_chinese: line.line_name_zh, name_korean: line.line_name_ko, color: line.line_color_c.unwrap_or_default(), - line_type: line.line_type.unwrap_or_default(), + line_type, line_symbols: line.line_symbols.into_iter().map(|s| s.into()).collect(), status: line.e_status, station: line.station.map(|s| Box::new(s.into())), @@ -34,3 +42,325 @@ fn convert_transport_type(t: TransportType) -> i32 { TransportType::Bus => GrpcTransportType::Bus as i32, } } + +#[cfg(test)] +mod tests { + use super::*; + use crate::proto::LineType as GrpcLineType; + + fn create_test_line(transport_type: TransportType, line_type: Option) -> Line { + Line { + line_cd: 1, + company_cd: 1, + company: None, + line_name: "テスト路線".to_string(), + line_name_k: "テストロセン".to_string(), + line_name_h: "てすとろせん".to_string(), + line_name_r: Some("Test Line".to_string()), + line_name_zh: Some("测试线路".to_string()), + line_name_ko: Some("테스트노선".to_string()), + line_color_c: Some("#FF0000".to_string()), + line_type, + line_symbols: vec![], + line_symbol1: None, + line_symbol2: None, + line_symbol3: None, + line_symbol4: None, + line_symbol1_color: None, + line_symbol2_color: None, + line_symbol3_color: None, + line_symbol4_color: None, + line_symbol1_shape: None, + line_symbol2_shape: None, + line_symbol3_shape: None, + line_symbol4_shape: None, + e_status: 0, + e_sort: 1, + station: None, + train_type: None, + line_group_cd: None, + station_cd: None, + station_g_cd: None, + average_distance: Some(1.5), + type_cd: None, + transport_type, + } + } + + // ============================================ + // バス路線の line_type 変換テスト + // ============================================ + + #[test] + fn test_bus_line_with_subway_line_type_returns_other() { + // バス路線にSubway(3)が設定されていても、OtherLineType(0)が返される + let bus_line = create_test_line(TransportType::Bus, Some(GrpcLineType::Subway as i32)); + let grpc_line: GrpcLine = bus_line.into(); + + assert_eq!(grpc_line.line_type, GrpcLineType::OtherLineType as i32); + assert_eq!(grpc_line.transport_type, GrpcTransportType::Bus as i32); + } + + #[test] + fn test_bus_line_with_bullet_train_line_type_returns_other() { + // バス路線にBulletTrain(1)が設定されていても、OtherLineType(0)が返される + let bus_line = create_test_line(TransportType::Bus, Some(GrpcLineType::BulletTrain as i32)); + let grpc_line: GrpcLine = bus_line.into(); + + assert_eq!(grpc_line.line_type, GrpcLineType::OtherLineType as i32); + } + + #[test] + fn test_bus_line_with_normal_line_type_returns_other() { + // バス路線にNormal(2)が設定されていても、OtherLineType(0)が返される + let bus_line = create_test_line(TransportType::Bus, Some(GrpcLineType::Normal as i32)); + let grpc_line: GrpcLine = bus_line.into(); + + assert_eq!(grpc_line.line_type, GrpcLineType::OtherLineType as i32); + } + + #[test] + fn test_bus_line_with_tram_line_type_returns_other() { + // バス路線にTram(4)が設定されていても、OtherLineType(0)が返される + let bus_line = create_test_line(TransportType::Bus, Some(GrpcLineType::Tram as i32)); + let grpc_line: GrpcLine = bus_line.into(); + + assert_eq!(grpc_line.line_type, GrpcLineType::OtherLineType as i32); + } + + #[test] + fn test_bus_line_with_monorail_line_type_returns_other() { + // バス路線にMonorailOrAGT(5)が設定されていても、OtherLineType(0)が返される + let bus_line = + create_test_line(TransportType::Bus, Some(GrpcLineType::MonorailOrAgt as i32)); + let grpc_line: GrpcLine = bus_line.into(); + + assert_eq!(grpc_line.line_type, GrpcLineType::OtherLineType as i32); + } + + #[test] + fn test_bus_line_with_none_line_type_returns_other() { + // バス路線でline_typeがNoneでも、OtherLineType(0)が返される + let bus_line = create_test_line(TransportType::Bus, None); + let grpc_line: GrpcLine = bus_line.into(); + + assert_eq!(grpc_line.line_type, GrpcLineType::OtherLineType as i32); + } + + #[test] + fn test_bus_line_with_other_line_type_returns_other() { + // バス路線でOtherLineType(0)が設定されていれば、そのままOtherLineType(0)が返される + let bus_line = + create_test_line(TransportType::Bus, Some(GrpcLineType::OtherLineType as i32)); + let grpc_line: GrpcLine = bus_line.into(); + + assert_eq!(grpc_line.line_type, GrpcLineType::OtherLineType as i32); + } + + // ============================================ + // 鉄道路線の line_type 変換テスト + // ============================================ + + #[test] + fn test_rail_line_with_subway_line_type_preserved() { + // 鉄道路線ではSubway(3)がそのまま返される + let rail_line = create_test_line(TransportType::Rail, Some(GrpcLineType::Subway as i32)); + let grpc_line: GrpcLine = rail_line.into(); + + assert_eq!(grpc_line.line_type, GrpcLineType::Subway as i32); + assert_eq!(grpc_line.transport_type, GrpcTransportType::Rail as i32); + } + + #[test] + fn test_rail_line_with_bullet_train_line_type_preserved() { + // 鉄道路線ではBulletTrain(1)がそのまま返される + let rail_line = + create_test_line(TransportType::Rail, Some(GrpcLineType::BulletTrain as i32)); + let grpc_line: GrpcLine = rail_line.into(); + + assert_eq!(grpc_line.line_type, GrpcLineType::BulletTrain as i32); + } + + #[test] + fn test_rail_line_with_normal_line_type_preserved() { + // 鉄道路線ではNormal(2)がそのまま返される + let rail_line = create_test_line(TransportType::Rail, Some(GrpcLineType::Normal as i32)); + let grpc_line: GrpcLine = rail_line.into(); + + assert_eq!(grpc_line.line_type, GrpcLineType::Normal as i32); + } + + #[test] + fn test_rail_line_with_tram_line_type_preserved() { + // 鉄道路線ではTram(4)がそのまま返される + let rail_line = create_test_line(TransportType::Rail, Some(GrpcLineType::Tram as i32)); + let grpc_line: GrpcLine = rail_line.into(); + + assert_eq!(grpc_line.line_type, GrpcLineType::Tram as i32); + } + + #[test] + fn test_rail_line_with_monorail_line_type_preserved() { + // 鉄道路線ではMonorailOrAGT(5)がそのまま返される + let rail_line = create_test_line( + TransportType::Rail, + Some(GrpcLineType::MonorailOrAgt as i32), + ); + let grpc_line: GrpcLine = rail_line.into(); + + assert_eq!(grpc_line.line_type, GrpcLineType::MonorailOrAgt as i32); + } + + #[test] + fn test_rail_line_with_none_line_type_defaults_to_other() { + // 鉄道路線でline_typeがNoneの場合、OtherLineType(0)がデフォルトで返される + let rail_line = create_test_line(TransportType::Rail, None); + let grpc_line: GrpcLine = rail_line.into(); + + assert_eq!(grpc_line.line_type, GrpcLineType::OtherLineType as i32); + } + + // ============================================ + // convert_transport_type 関数のテスト + // ============================================ + + #[test] + fn test_convert_transport_type_rail() { + let result = convert_transport_type(TransportType::Rail); + assert_eq!(result, GrpcTransportType::Rail as i32); + } + + #[test] + fn test_convert_transport_type_bus() { + let result = convert_transport_type(TransportType::Bus); + assert_eq!(result, GrpcTransportType::Bus as i32); + } + + // ============================================ + // Line から GrpcLine への変換テスト (その他フィールド) + // ============================================ + + #[test] + fn test_line_to_grpc_line_basic_fields() { + let line = create_test_line(TransportType::Rail, Some(GrpcLineType::Normal as i32)); + let grpc_line: GrpcLine = line.into(); + + assert_eq!(grpc_line.id, 1); + assert_eq!(grpc_line.name_short, "テスト路線"); + assert_eq!(grpc_line.name_katakana, "テストロセン"); + assert_eq!(grpc_line.name_full, "てすとろせん"); + assert_eq!(grpc_line.name_roman, Some("Test Line".to_string())); + assert_eq!(grpc_line.name_chinese, Some("测试线路".to_string())); + assert_eq!(grpc_line.name_korean, Some("테스트노선".to_string())); + assert_eq!(grpc_line.color, "#FF0000"); + assert_eq!(grpc_line.status, 0); + assert!((grpc_line.average_distance - 1.5).abs() < f64::EPSILON); + } + + #[test] + fn test_line_to_grpc_line_with_none_optional_fields() { + let mut line = create_test_line(TransportType::Rail, None); + line.line_name_r = None; + line.line_name_zh = None; + line.line_name_ko = None; + line.line_color_c = None; + line.average_distance = None; + + let grpc_line: GrpcLine = line.into(); + + // name_romanはNoneでもSome("")になる + assert_eq!(grpc_line.name_roman, Some("".to_string())); + assert_eq!(grpc_line.name_chinese, None); + assert_eq!(grpc_line.name_korean, None); + // colorはNoneでも""になる + assert_eq!(grpc_line.color, ""); + // average_distanceはNoneでも0.0になる + assert!((grpc_line.average_distance - 0.0).abs() < f64::EPSILON); + } + + #[test] + fn test_line_to_grpc_line_empty_line_symbols() { + let line = create_test_line(TransportType::Rail, None); + let grpc_line: GrpcLine = line.into(); + + assert!(grpc_line.line_symbols.is_empty()); + } + + #[test] + fn test_line_to_grpc_line_without_station() { + let line = create_test_line(TransportType::Rail, None); + let grpc_line: GrpcLine = line.into(); + + assert!(grpc_line.station.is_none()); + } + + #[test] + fn test_line_to_grpc_line_without_company() { + let line = create_test_line(TransportType::Rail, None); + let grpc_line: GrpcLine = line.into(); + + assert!(grpc_line.company.is_none()); + } + + #[test] + fn test_line_to_grpc_line_without_train_type() { + let line = create_test_line(TransportType::Rail, None); + let grpc_line: GrpcLine = line.into(); + + assert!(grpc_line.train_type.is_none()); + } + + // ============================================ + // 境界値・エッジケースのテスト + // ============================================ + + #[test] + fn test_line_type_boundary_values() { + // line_typeの境界値テスト + let test_cases = vec![ + (0, GrpcLineType::OtherLineType as i32), + (1, GrpcLineType::BulletTrain as i32), + (2, GrpcLineType::Normal as i32), + (3, GrpcLineType::Subway as i32), + (4, GrpcLineType::Tram as i32), + (5, GrpcLineType::MonorailOrAgt as i32), + ]; + + for (input, expected) in test_cases { + let line = create_test_line(TransportType::Rail, Some(input)); + let grpc_line: GrpcLine = line.into(); + assert_eq!( + grpc_line.line_type, expected, + "Failed for line_type: {input}" + ); + } + } + + #[test] + fn test_line_type_unknown_value_preserved_for_rail() { + // 鉄道路線で未知のline_type値は保持される + let line = create_test_line(TransportType::Rail, Some(99)); + let grpc_line: GrpcLine = line.into(); + + assert_eq!(grpc_line.line_type, 99); + } + + #[test] + fn test_bus_line_unknown_line_type_returns_other() { + // バス路線では未知のline_type値もOtherLineTypeになる + let line = create_test_line(TransportType::Bus, Some(99)); + let grpc_line: GrpcLine = line.into(); + + assert_eq!(grpc_line.line_type, GrpcLineType::OtherLineType as i32); + } + + #[test] + fn test_line_cd_to_id_conversion() { + // line_cdがu32に正しく変換されることを確認 + let mut line = create_test_line(TransportType::Rail, None); + line.line_cd = 12345; + let grpc_line: GrpcLine = line.into(); + + assert_eq!(grpc_line.id, 12345); + } +} diff --git a/stationapi/src/use_case/interactor/query.rs b/stationapi/src/use_case/interactor/query.rs index 57bdcdbe..67043a9e 100644 --- a/stationapi/src/use_case/interactor/query.rs +++ b/stationapi/src/use_case/interactor/query.rs @@ -202,7 +202,7 @@ where let stations = self .station_repository .get_by_name( - normalize_for_search(&station_name), + station_name, limit, from_station_group_id, filter_to_db_type(transport_type), From c82034e0501949aa965d0b77d942826910c0bfe9 Mon Sep 17 00:00:00 2001 From: Tsubasa SEKIGUCHI Date: Sun, 11 Jan 2026 07:30:26 +0900 Subject: [PATCH 3/6] =?UTF-8?q?=E8=A5=BF=E9=89=84=E3=83=87=E3=83=BC?= =?UTF-8?q?=E3=82=BF=E6=9B=B4=E6=96=B0=20(#1375)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * 春日原駅を特急停車駅に指定 * 聖マリア病院前の駅名修正 --- data/3!stations.csv | 2 +- data/5!station_station_types.csv | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/data/3!stations.csv b/data/3!stations.csv index 42a0b25d..d0e58a9b 100644 --- a/data/3!stations.csv +++ b/data/3!stations.csv @@ -7292,7 +7292,7 @@ station_cd,station_g_cd,station_name,station_name_k,station_name_r,station_name_ 3600124,3600124,櫛原,クシワラ,Kushiwara,Kushiwara,栉原,구시와라,26,,,,,36001,40,830-0013,福岡県久留米市櫛原町原口1516-4,130.524319,33.319592,0000-00-00,0000-00-00,0,3600124 3600125,3600125,西鉄久留米,ニシテツクルメ,Nishitetsu-Kurume,Nishitetsu-Kurume,西铁久留米,니시테쓰쿠루메,27,,,,,36001,40,830-0032,福岡県久留米市東町309-2,130.521053,33.312276,0000-00-00,0000-00-00,0,3600125 3600126,3600126,花畑,ハナバタケ,Hanabatake,Hanabatake,花畑,하나바타케,28,,,,,36001,40,830-0038,福岡県久留米市西町968,130.515972,33.306643,0000-00-00,0000-00-00,0,3600126 -3600127,3600127,聖マリア病院前,セイマリアビョウインマエ,Shikenjo-Mae,Shikenjo-Mae,试验场前,시켄조마에,29,,,,,36001,40,830-0047,福岡県久留米市津福本町235,130.509923,33.301985,0000-00-00,0000-00-00,0,3600127 +3600127,3600127,聖マリア病院前,セイマリアビョウインマエ,St. Mary's Hospital,St. Mary's Hospital,圣玛丽病院前,성마리아병원마에,29,,,,,36001,40,830-0047,福岡県久留米市津福本町235,130.509923,33.301985,0000-00-00,0000-00-00,0,3600127 3600128,3600128,津福,ツブク,Tsubuku,Tsubuku,津福,쓰부쿠,30,,,,,36001,40,830-0047,福岡県久留米市津福本町1587,130.497788,33.296505,0000-00-00,0000-00-00,0,3600128 3600129,3600129,安武,ヤスタケ,Yasutake,Yasutake,安武,야스타케,31,,,,,36001,40,830-0072,福岡県久留米市安武町安武本3327,130.489061,33.286311,0000-00-00,0000-00-00,0,3600129 3600130,3600130,大善寺,ダイゼンジ,Daisenji,Daisenji,大善寺,다이젠지,32,,,,,36001,40,830-0073,福岡県久留米市大善寺町宮本1173,130.473897,33.270432,0000-00-00,0000-00-00,0,3600130 diff --git a/data/5!station_station_types.csv b/data/5!station_station_types.csv index 4a6f16b4..187b871b 100644 --- a/data/5!station_station_types.csv +++ b/data/5!station_station_types.csv @@ -10058,7 +10058,7 @@ DEFAULT,3600105,306,267,0,大橋 DEFAULT,3600106,306,267,1,井尻 DEFAULT,3600107,306,267,1,雑餉隈 DEFAULT,3600150,306,267,1,桜並木 -DEFAULT,3600108,306,267,1,春日原 +DEFAULT,3600108,306,267,0,春日原 DEFAULT,3600109,306,267,1,白木原 DEFAULT,3600110,306,267,1,下大利 DEFAULT,3600111,306,267,1,都府楼前 From 5303b7bae525461f81946ddd2752f3a38bb6d0be Mon Sep 17 00:00:00 2001 From: Tsubasa SEKIGUCHI Date: Mon, 9 Feb 2026 07:23:44 +0900 Subject: [PATCH 4/6] master<-dev (#1391) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * GetStationsByLineIdList RPC追加 複数のline_idを一括で処理するバッチRPCを追加。 既存のGetStationByIdList/GetLineByIdListと同じバッチパターンに従う。 Co-Authored-By: Claude Opus 4.6 * cargo fmt * get_by_line_id_vecのORDER BYを入力順序保持のCASE式に変更 Co-Authored-By: Claude Opus 4.6 * Merge pull request #1389 from TrainLCD/feature/tt-batch GetStationsByLineGroupIdList RPC追加 * get_stations_by_line_group_id_vecで列車種別を一括取得してセット (#1390) * get_stations_by_line_group_id_vecで列車種別を一括取得してセット N+1問題を回避しつつ、train_type_repository.get_by_line_group_id_vecで 複数line_group_idの列車種別を1クエリで取得し、top-levelとlines[].station の両方にセットするよう修正。 Co-Authored-By: Claude Opus 4.6 * get_stations_by_line_group_id_vecのユニットテストを追加 Co-Authored-By: Claude Opus 4.6 * ネストされた駅のtrain_typeをis_some()で明示的に検証するよう修正 Co-Authored-By: Claude Opus 4.6 * train_typeをtrain_type_mapから常に上書きしてリーク防止 Co-Authored-By: Claude Opus 4.6 --------- Co-authored-by: Claude Opus 4.6 --------- Co-authored-by: Claude Opus 4.6 --- stationapi/proto | 2 +- .../domain/repository/station_repository.rs | 33 +++ .../src/infrastructure/station_repository.rs | 223 +++++++++++++++++ .../src/presentation/controller/grpc.rs | 67 ++++- stationapi/src/use_case/interactor/query.rs | 231 +++++++++++++++++- stationapi/src/use_case/traits/query.rs | 10 + 6 files changed, 560 insertions(+), 6 deletions(-) diff --git a/stationapi/proto b/stationapi/proto index 0e77c9f2..7eede39e 160000 --- a/stationapi/proto +++ b/stationapi/proto @@ -1 +1 @@ -Subproject commit 0e77c9f216605475d3b8ed7e526df8d8c8d223c6 +Subproject commit 7eede39eb74fac9cde5e3ed0535cc1aff8b95580 diff --git a/stationapi/src/domain/repository/station_repository.rs b/stationapi/src/domain/repository/station_repository.rs index 7e28cc0f..151c24a3 100644 --- a/stationapi/src/domain/repository/station_repository.rs +++ b/stationapi/src/domain/repository/station_repository.rs @@ -15,6 +15,7 @@ pub trait StationRepository: Send + Sync + 'static { station_id: Option, direction_id: Option, ) -> Result, DomainError>; + async fn get_by_line_id_vec(&self, line_ids: &[u32]) -> Result, DomainError>; async fn get_by_station_group_id( &self, station_group_id: u32, @@ -38,6 +39,10 @@ pub trait StationRepository: Send + Sync + 'static { transport_type: Option, ) -> Result, DomainError>; async fn get_by_line_group_id(&self, line_group_id: u32) -> Result, DomainError>; + async fn get_by_line_group_id_vec( + &self, + line_group_ids: &[u32], + ) -> Result, DomainError>; async fn get_route_stops( &self, from_station_id: u32, @@ -107,6 +112,16 @@ mod tests { Ok(result) } + async fn get_by_line_id_vec(&self, line_ids: &[u32]) -> Result, DomainError> { + let result: Vec = self + .stations + .values() + .filter(|station| line_ids.contains(&(station.line_cd as u32))) + .cloned() + .collect(); + Ok(result) + } + async fn get_by_station_group_id( &self, station_group_id: u32, @@ -208,6 +223,24 @@ mod tests { Ok(result) } + async fn get_by_line_group_id_vec( + &self, + line_group_ids: &[u32], + ) -> Result, DomainError> { + let result: Vec = self + .stations + .values() + .filter(|station| { + station + .line_group_cd + .map(|v| line_group_ids.contains(&(v as u32))) + .unwrap_or(false) + }) + .cloned() + .collect(); + Ok(result) + } + async fn get_route_stops( &self, from_station_id: u32, diff --git a/stationapi/src/infrastructure/station_repository.rs b/stationapi/src/infrastructure/station_repository.rs index 69c913fe..d1615b68 100644 --- a/stationapi/src/infrastructure/station_repository.rs +++ b/stationapi/src/infrastructure/station_repository.rs @@ -210,6 +210,10 @@ impl StationRepository for MyStationRepository { } } } + async fn get_by_line_id_vec(&self, line_ids: &[u32]) -> Result, DomainError> { + let mut conn = self.pool.acquire().await?; + InternalStationRepository::get_by_line_id_vec(line_ids, &mut conn).await + } async fn get_by_station_group_id( &self, station_group_id: u32, @@ -268,6 +272,14 @@ impl StationRepository for MyStationRepository { InternalStationRepository::get_by_line_group_id(line_group_id, &mut conn).await } + async fn get_by_line_group_id_vec( + &self, + line_group_ids: &[u32], + ) -> Result, DomainError> { + let mut conn = self.pool.acquire().await?; + InternalStationRepository::get_by_line_group_id_vec(line_group_ids, &mut conn).await + } + async fn get_route_stops( &self, from_station_id: u32, @@ -607,6 +619,111 @@ impl InternalStationRepository { Ok(stations) } + async fn get_by_line_id_vec( + line_ids: &[u32], + conn: &mut PgConnection, + ) -> Result, DomainError> { + if line_ids.is_empty() { + return Ok(vec![]); + } + + let params = (1..=line_ids.len()) + .map(|i| format!("${i}")) + .collect::>() + .join(", "); + + let order_case = line_ids + .iter() + .enumerate() + .map(|(i, _)| format!("WHEN ${} THEN {}", i + 1 + line_ids.len(), i)) + .collect::>() + .join(" "); + + let query_str = format!( + r#"SELECT + s.station_cd, + s.station_g_cd, + s.station_name, + s.station_name_k, + s.station_name_r, + s.station_name_rn, + s.station_name_zh, + s.station_name_ko, + s.station_number1, + s.station_number2, + s.station_number3, + s.station_number4, + s.three_letter_code, + s.line_cd, + s.pref_cd, + s.post, + s.address, + s.lon, + s.lat, + s.open_ymd, + s.close_ymd, + s.e_status, + s.e_sort, + l.company_cd, + COALESCE(NULLIF(COALESCE(a.line_name, l.line_name), ''), NULL) AS line_name, + COALESCE(NULLIF(COALESCE(a.line_name_k, l.line_name_k), ''), NULL) AS line_name_k, + COALESCE(NULLIF(COALESCE(a.line_name_h, l.line_name_h), ''), NULL) AS line_name_h, + COALESCE(NULLIF(COALESCE(a.line_name_r, l.line_name_r), ''), NULL) AS line_name_r, + COALESCE(NULLIF(COALESCE(a.line_name_zh, l.line_name_zh), ''), NULL) AS line_name_zh, + COALESCE(NULLIF(COALESCE(a.line_name_ko, l.line_name_ko), ''), NULL) AS line_name_ko, + COALESCE(NULLIF(COALESCE(a.line_color_c, l.line_color_c), ''), NULL) AS line_color_c, + l.line_type, + l.line_symbol1, + l.line_symbol2, + l.line_symbol3, + l.line_symbol4, + l.line_symbol1_color, + l.line_symbol2_color, + l.line_symbol3_color, + l.line_symbol4_color, + l.line_symbol1_shape, + l.line_symbol2_shape, + l.line_symbol3_shape, + l.line_symbol4_shape, + COALESCE(l.average_distance, 0.0)::DOUBLE PRECISION AS average_distance, + NULL::int AS type_id, + NULL::int AS sst_id, + NULL::int AS type_cd, + NULL::int AS line_group_cd, + NULL::int AS pass, + NULL::text AS type_name, + NULL::text AS type_name_k, + NULL::text AS type_name_r, + NULL::text AS type_name_zh, + NULL::text AS type_name_ko, + NULL::text AS color, + NULL::int AS direction, + NULL::int AS kind, + s.transport_type + FROM stations AS s + JOIN lines AS l ON l.line_cd = s.line_cd + LEFT JOIN line_aliases AS la ON la.station_cd = s.station_cd + LEFT JOIN aliases AS a ON a.id = la.alias_cd + WHERE l.line_cd IN ( {params} ) + AND s.e_status = 0 + AND l.e_status = 0 + ORDER BY CASE l.line_cd {order_case} END, s.e_sort ASC, s.station_cd ASC"# + ); + + let mut query = sqlx::query_as::<_, StationRow>(&query_str); + for id in line_ids { + query = query.bind(*id as i32); + } + for id in line_ids { + query = query.bind(*id as i32); + } + + let rows = query.fetch_all(conn).await?; + let stations: Vec = rows.into_iter().map(|row| row.into()).collect(); + + Ok(stations) + } + async fn get_by_line_id_and_station_id( line_id: u32, station_id: u32, @@ -1234,6 +1351,112 @@ impl InternalStationRepository { Ok(stations) } + async fn get_by_line_group_id_vec( + line_group_ids: &[u32], + conn: &mut PgConnection, + ) -> Result, DomainError> { + if line_group_ids.is_empty() { + return Ok(vec![]); + } + + let params = (1..=line_group_ids.len()) + .map(|i| format!("${i}")) + .collect::>() + .join(", "); + + let order_case = line_group_ids + .iter() + .enumerate() + .map(|(i, _)| format!("WHEN ${} THEN {}", i + 1 + line_group_ids.len(), i)) + .collect::>() + .join(" "); + + let query_str = format!( + r#"SELECT + s.station_cd, + s.station_g_cd, + s.station_name, + s.station_name_k, + s.station_name_r, + s.station_name_rn, + s.station_name_zh, + s.station_name_ko, + s.station_number1, + s.station_number2, + s.station_number3, + s.station_number4, + s.three_letter_code, + s.line_cd, + s.pref_cd, + s.post, + s.address, + s.lon, + s.lat, + s.open_ymd, + s.close_ymd, + s.e_status, + s.e_sort, + COALESCE(NULLIF(COALESCE(a.line_name, l.line_name), ''), NULL) AS line_name, + COALESCE(NULLIF(COALESCE(a.line_name_k, l.line_name_k), ''), NULL) AS line_name_k, + COALESCE(NULLIF(COALESCE(a.line_name_h, l.line_name_h), ''), NULL) AS line_name_h, + COALESCE(NULLIF(COALESCE(a.line_name_r, l.line_name_r), ''), NULL) AS line_name_r, + COALESCE(NULLIF(COALESCE(a.line_name_zh, l.line_name_zh), ''), NULL) AS line_name_zh, + COALESCE(NULLIF(COALESCE(a.line_name_ko, l.line_name_ko), ''), NULL) AS line_name_ko, + COALESCE(NULLIF(COALESCE(a.line_color_c, l.line_color_c), ''), NULL) AS line_color_c, + l.company_cd, + l.line_type, + l.line_symbol1, + l.line_symbol2, + l.line_symbol3, + l.line_symbol4, + l.line_symbol1_color, + l.line_symbol2_color, + l.line_symbol3_color, + l.line_symbol4_color, + l.line_symbol1_shape, + l.line_symbol2_shape, + l.line_symbol3_shape, + l.line_symbol4_shape, + COALESCE(l.average_distance, 0.0)::DOUBLE PRECISION AS average_distance, + sst.id AS sst_id, + sst.type_cd, + sst.line_group_cd, + sst.pass, + t.id AS type_id, + t.type_name, + t.type_name_k, + t.type_name_r, + t.type_name_zh, + t.type_name_ko, + t.color, + t.direction, + t.kind, + s.transport_type + FROM stations AS s + JOIN lines AS l ON l.line_cd = s.line_cd AND l.e_status = 0 + JOIN station_station_types AS sst ON sst.line_group_cd IN ( {params} ) AND sst.station_cd = s.station_cd + JOIN types AS t ON t.type_cd = sst.type_cd + LEFT JOIN line_aliases AS la ON la.station_cd = s.station_cd + LEFT JOIN aliases AS a ON a.id = la.alias_cd + WHERE + s.e_status = 0 + ORDER BY CASE sst.line_group_cd {order_case} END, sst.id"# + ); + + let mut query = sqlx::query_as::<_, StationRow>(&query_str); + for id in line_group_ids { + query = query.bind(*id as i32); + } + for id in line_group_ids { + query = query.bind(*id as i32); + } + + let rows = query.fetch_all(conn).await?; + let stations: Vec = rows.into_iter().map(|row| row.into()).collect(); + + Ok(stations) + } + async fn get_route_stops( from_station_id: u32, to_station_id: u32, diff --git a/stationapi/src/presentation/controller/grpc.rs b/stationapi/src/presentation/controller/grpc.rs index 77ba0a27..846d5f86 100644 --- a/stationapi/src/presentation/controller/grpc.rs +++ b/stationapi/src/presentation/controller/grpc.rs @@ -9,10 +9,12 @@ use crate::{ station_api_server::StationApi, GetConnectedStationsRequest, GetLineByIdListRequest, GetLineByIdRequest, GetLinesByNameRequest, GetRouteRequest, GetStationByCoordinatesRequest, GetStationByGroupIdRequest, GetStationByIdListRequest, GetStationByIdRequest, - GetStationByLineIdRequest, GetStationsByLineGroupIdRequest, GetStationsByNameRequest, - GetTrainTypesByStationIdRequest, MultipleLineResponse, MultipleStationResponse, - MultipleTrainTypeResponse, Route, RouteMinimalResponse, RouteResponse, RouteTypeResponse, - SingleLineResponse, SingleStationResponse, TransportType as GrpcTransportType, + GetStationByLineIdListRequest, GetStationByLineIdRequest, + GetStationsByLineGroupIdListRequest, GetStationsByLineGroupIdRequest, + GetStationsByNameRequest, GetTrainTypesByStationIdRequest, MultipleLineResponse, + MultipleStationResponse, MultipleTrainTypeResponse, Route, RouteMinimalResponse, + RouteResponse, RouteTypeResponse, SingleLineResponse, SingleStationResponse, + TransportType as GrpcTransportType, }, use_case::{interactor::query::QueryInteractor, traits::query::QueryUseCase}, }; @@ -159,6 +161,27 @@ impl StationApi for MyApi { Err(err) => Err(PresentationalError::from(err).into()), } } + + async fn get_stations_by_line_id_list( + &self, + request: tonic::Request, + ) -> Result, tonic::Status> { + let request_ref = request.get_ref(); + let line_ids = &request_ref.line_ids; + let transport_type = convert_transport_type(request_ref.transport_type); + + match self + .query_use_case + .get_stations_by_line_id_vec(line_ids, transport_type) + .await + { + Ok(stations) => Ok(Response::new(MultipleStationResponse { + stations: stations.into_iter().map(|station| station.into()).collect(), + })), + Err(err) => Err(PresentationalError::from(err).into()), + } + } + async fn get_stations_by_name( &self, request: tonic::Request, @@ -209,6 +232,26 @@ impl StationApi for MyApi { } } + async fn get_stations_by_line_group_id_list( + &self, + request: tonic::Request, + ) -> Result, tonic::Status> { + let request_ref = request.get_ref(); + let line_group_ids = &request_ref.line_group_ids; + let transport_type = convert_transport_type(request_ref.transport_type); + + match self + .query_use_case + .get_stations_by_line_group_id_vec(line_group_ids, transport_type) + .await + { + Ok(stations) => Ok(Response::new(MultipleStationResponse { + stations: stations.into_iter().map(|station| station.into()).collect(), + })), + Err(err) => Err(PresentationalError::from(err).into()), + } + } + async fn get_train_types_by_station_id( &self, request: tonic::Request, @@ -624,6 +667,14 @@ mod tests { Ok(vec![]) } + async fn get_stations_by_line_id_vec( + &self, + _line_ids: &[u32], + _transport_type: TransportTypeFilter, + ) -> Result, UseCaseError> { + Ok(vec![]) + } + async fn get_stations_by_name( &self, _station_name: String, @@ -735,6 +786,14 @@ mod tests { Ok(vec![]) } + async fn get_stations_by_line_group_id_vec( + &self, + _line_group_ids: &[u32], + _transport_type: TransportTypeFilter, + ) -> Result, UseCaseError> { + Ok(vec![]) + } + async fn get_train_types_by_station_id( &self, _station_id: u32, diff --git a/stationapi/src/use_case/interactor/query.rs b/stationapi/src/use_case/interactor/query.rs index 67043a9e..bcaafd23 100644 --- a/stationapi/src/use_case/interactor/query.rs +++ b/stationapi/src/use_case/interactor/query.rs @@ -192,6 +192,24 @@ where Ok(stations) } + async fn get_stations_by_line_id_vec( + &self, + line_ids: &[u32], + transport_type: TransportTypeFilter, + ) -> Result, UseCaseError> { + let stations = self.station_repository.get_by_line_id_vec(line_ids).await?; + + let stations: Vec = stations + .into_iter() + .filter(|s| matches_transport_filter(s.transport_type, transport_type)) + .collect(); + + let stations = self + .update_station_vec_with_attributes(stations, None, transport_type) + .await?; + + Ok(stations) + } async fn get_stations_by_name( &self, station_name: String, @@ -371,6 +389,55 @@ where Ok(stations) } + async fn get_stations_by_line_group_id_vec( + &self, + line_group_ids: &[u32], + transport_type: TransportTypeFilter, + ) -> Result, UseCaseError> { + let stations = self + .station_repository + .get_by_line_group_id_vec(line_group_ids) + .await?; + + let stations: Vec = stations + .into_iter() + .filter(|s| matches_transport_filter(s.transport_type, transport_type)) + .collect(); + + // lines, companies, station_numbers等を付与(train_typeはNoneで空になる) + let mut stations = self + .update_station_vec_with_attributes(stations, None, transport_type) + .await?; + + // 複数line_group_idの列車種別を一括取得してセット + let train_types = self + .train_type_repository + .get_by_line_group_id_vec(line_group_ids) + .await?; + let train_type_map: std::collections::HashMap = train_types + .iter() + .filter_map(|tt| tt.station_cd.map(|cd| (cd, tt))) + .collect(); + + for station in stations.iter_mut() { + station.train_type = train_type_map + .get(&station.station_cd) + .cloned() + .cloned() + .map(Box::new); + for line in station.lines.iter_mut() { + if let Some(ref mut nested_station) = line.station { + nested_station.train_type = train_type_map + .get(&nested_station.station_cd) + .cloned() + .cloned() + .map(Box::new); + } + } + } + + Ok(stations) + } fn get_station_numbers(&self, station: &Station) -> Vec { let line_symbols_raw = [ &station.line_symbol1, @@ -1319,6 +1386,9 @@ mod tests { ) -> Result, DomainError> { Ok(vec![]) } + async fn get_by_line_id_vec(&self, _: &[u32]) -> Result, DomainError> { + Ok(vec![]) + } async fn get_by_station_group_id(&self, _: u32) -> Result, DomainError> { Ok(vec![]) } @@ -1349,6 +1419,12 @@ mod tests { async fn get_by_line_group_id(&self, _: u32) -> Result, DomainError> { Ok(vec![]) } + async fn get_by_line_group_id_vec( + &self, + _: &[u32], + ) -> Result, DomainError> { + Ok(vec![]) + } async fn get_route_stops( &self, _: u32, @@ -1676,6 +1752,7 @@ mod tests { struct ConfigurableMockStationRepository { stations_by_group: Vec, bus_stops: Vec, + stations_by_line_group: Vec, } impl ConfigurableMockStationRepository { @@ -1683,8 +1760,14 @@ mod tests { Self { stations_by_group, bus_stops, + stations_by_line_group: vec![], } } + + fn with_line_group_stations(mut self, stations: Vec) -> Self { + self.stations_by_line_group = stations; + self + } } #[async_trait::async_trait] @@ -1703,6 +1786,9 @@ mod tests { ) -> Result, DomainError> { Ok(vec![]) } + async fn get_by_line_id_vec(&self, _: &[u32]) -> Result, DomainError> { + Ok(vec![]) + } async fn get_by_station_group_id(&self, _: u32) -> Result, DomainError> { Ok(vec![]) } @@ -1738,6 +1824,12 @@ mod tests { async fn get_by_line_group_id(&self, _: u32) -> Result, DomainError> { Ok(vec![]) } + async fn get_by_line_group_id_vec( + &self, + _: &[u32], + ) -> Result, DomainError> { + Ok(self.stations_by_line_group.clone()) + } async fn get_route_stops( &self, _: u32, @@ -1846,7 +1938,7 @@ mod tests { &self, _: &[u32], ) -> Result, DomainError> { - Ok(vec![]) + Ok(self.train_types.clone()) } } @@ -2394,6 +2486,143 @@ mod tests { assert!(!line.line_symbols.is_empty()); } } + + #[tokio::test] + async fn test_get_stations_by_line_group_id_vec_populates_train_type() { + let company = create_test_company(1, "JR東日本"); + + let mut station1 = create_test_station(101, 1001, 100, Some(1000)); + station1.company_cd = Some(1); + + let mut station2 = create_test_station(201, 2001, 200, Some(2000)); + station2.company_cd = Some(1); + + let train_type1 = create_test_train_type_for_station(101, "快速"); + let mut train_type2 = create_test_train_type_for_station(201, "特急"); + train_type2.line_group_cd = Some(2000); + + let line1 = create_test_line_for_station_group(100, 1001); + let line2 = create_test_line_for_station_group(200, 2001); + + let station_repo = ConfigurableMockStationRepository::new( + vec![station1.clone(), station2.clone()], + vec![], + ) + .with_line_group_stations(vec![station1.clone(), station2.clone()]); + + let interactor = QueryInteractor { + station_repository: station_repo, + line_repository: ConfigurableMockLineRepository::new(vec![line1, line2]), + train_type_repository: ConfigurableMockTrainTypeRepository::new(vec![ + train_type1.clone(), + train_type2.clone(), + ]), + company_repository: ConfigurableMockCompanyRepository::new(vec![company]), + }; + + let result = interactor + .get_stations_by_line_group_id_vec(&[1000, 2000], TransportTypeFilter::Rail) + .await + .expect("Should succeed"); + + assert_eq!(result.len(), 2); + + // top-level train_type が設定されていること + let s1 = result.iter().find(|s| s.station_cd == 101).unwrap(); + assert!( + s1.train_type.is_some(), + "station 101 should have train_type" + ); + assert_eq!(s1.train_type.as_ref().unwrap().type_name, "快速"); + assert_eq!(s1.train_type.as_ref().unwrap().station_cd, Some(101)); + + let s2 = result.iter().find(|s| s.station_cd == 201).unwrap(); + assert!( + s2.train_type.is_some(), + "station 201 should have train_type" + ); + assert_eq!(s2.train_type.as_ref().unwrap().type_name, "特急"); + assert_eq!(s2.train_type.as_ref().unwrap().station_cd, Some(201)); + } + + #[tokio::test] + async fn test_get_stations_by_line_group_id_vec_populates_nested_train_type() { + let company = create_test_company(1, "JR東日本"); + + let mut station = create_test_station(101, 1001, 100, Some(1000)); + station.company_cd = Some(1); + + // 同じstation_g_cdで別路線のstation(lines配列内のネスト先になる) + let mut station_other_line = create_test_station(102, 1001, 200, Some(1000)); + station_other_line.company_cd = Some(1); + + let train_type1 = create_test_train_type_for_station(101, "快速"); + let train_type2 = create_test_train_type_for_station(102, "快速"); + + let line1 = create_test_line_for_station_group(100, 1001); + let line2 = create_test_line_for_station_group(200, 1001); + + let station_repo = ConfigurableMockStationRepository::new( + vec![station.clone(), station_other_line.clone()], + vec![], + ) + .with_line_group_stations(vec![station.clone()]); + + let interactor = QueryInteractor { + station_repository: station_repo, + line_repository: ConfigurableMockLineRepository::new(vec![line1, line2]), + train_type_repository: ConfigurableMockTrainTypeRepository::new(vec![ + train_type1.clone(), + train_type2.clone(), + ]), + company_repository: ConfigurableMockCompanyRepository::new(vec![company]), + }; + + let result = interactor + .get_stations_by_line_group_id_vec(&[1000], TransportTypeFilter::Rail) + .await + .expect("Should succeed"); + + assert_eq!(result.len(), 1); + + let station_result = &result[0]; + // top-level train_type + assert!( + station_result.train_type.is_some(), + "top-level station should have train_type" + ); + assert_eq!( + station_result.train_type.as_ref().unwrap().type_name, + "快速" + ); + + // nested stations in lines[] should also have train_type + for line in &station_result.lines { + if let Some(ref nested_station) = line.station { + assert!( + nested_station.train_type.is_some(), + "nested station (station_cd={}) should have train_type", + nested_station.station_cd + ); + assert_eq!( + nested_station.train_type.as_ref().unwrap().type_name, + "快速" + ); + } + } + } + + #[tokio::test] + async fn test_get_stations_by_line_group_id_vec_empty_input() { + let interactor = create_configurable_interactor(vec![], vec![], vec![], vec![], vec![]); + + let result = interactor + .get_stations_by_line_group_id_vec(&[], TransportTypeFilter::Rail) + .await + .expect("Should succeed"); + + assert!(result.is_empty()); + } } // ======================================== diff --git a/stationapi/src/use_case/traits/query.rs b/stationapi/src/use_case/traits/query.rs index 4c69673d..d134c7d4 100644 --- a/stationapi/src/use_case/traits/query.rs +++ b/stationapi/src/use_case/traits/query.rs @@ -44,6 +44,11 @@ pub trait QueryUseCase: Send + Sync + 'static { direction_id: Option, transport_type: TransportTypeFilter, ) -> Result, UseCaseError>; + async fn get_stations_by_line_id_vec( + &self, + line_ids: &[u32], + transport_type: TransportTypeFilter, + ) -> Result, UseCaseError>; async fn get_stations_by_name( &self, station_name: String, @@ -77,6 +82,11 @@ pub trait QueryUseCase: Send + Sync + 'static { line_group_id: u32, transport_type: TransportTypeFilter, ) -> Result, UseCaseError>; + async fn get_stations_by_line_group_id_vec( + &self, + line_group_ids: &[u32], + transport_type: TransportTypeFilter, + ) -> Result, UseCaseError>; async fn get_train_types_by_station_id( &self, station_id: u32, From 1cf0b8169e7b02740621ffb8f388b958cc0b09a9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E3=81=BF=E3=81=A3=E3=81=9F=E3=82=93?= <147319703+mittan12@users.noreply.github.com> Date: Thu, 5 Mar 2026 15:54:31 +0900 Subject: [PATCH 5/6] =?UTF-8?q?=E8=BF=91=E9=89=84=E7=A8=AE=E5=88=A5?= =?UTF-8?q?=E4=BF=AE=E6=AD=A3?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- data/4!types.csv | 4 + data/5!station_station_types.csv | 1148 +++++++++++++++--------------- 2 files changed, 559 insertions(+), 593 deletions(-) diff --git a/data/4!types.csv b/data/4!types.csv index 208bfb18..898bd2f1 100644 --- a/data/4!types.csv +++ b/data/4!types.csv @@ -263,6 +263,10 @@ Yokokawa","SL群马 254,371,A特急,Aトッキュウ,A Limited Express,A特快,A특급,#DC143C,0,4,0,,COMPLETE 255,372,B特急,Bトッキュウ,B Limited Express,B特快,B특급,#DC143C,0,4,0,,COMPLETE 202,373,区間準急,クカンジュンキュウ,Sub. Semi-Express,区间准急,구간준급,#00FF00,0,3,0,近鉄奈良線・難波線・大阪線、阪神なんば線・阪神本線用,PARTIAL +221,374,区間急行,クカンキュウコウ,Sub. Express,区间快速,구간급행,#F7931D,0,3,0,近鉄用,COMPLETE +228,375,急行,キュウコウ,Express,特快,급행,#F7931D,0,3,0,近鉄用,COMPLETE +213,376,快速急行,カイソクキュウコウ,Rapid Express,快速急行,쾌속급행,#DC143C,0,3,0,近鉄用,COMPLETE +213,377,快速急行,カイソクキュウコウ,Rapid Express,快速急行,쾌속급행,#00BFFF,0,3,0,阪神なんば線・阪神本線用,COMPLETE 256,400,京王ライナー(京王八王子方面),ケイオウライナー,Keio Liner(for Keiō-hachiōji),京王Liner,게이오 라이너,#DD0077,0,2,0,,COMPLETE 257,401,京王ライナー(橋本方面),ケイオウライナー,Keio Liner(for Hashimoto),京王Liner,게이오 라이너,#DD0078,0,2,0,,COMPLETE 258,402,モーニングウィング号,モーニングウィングゴウ,"Morning Wing(Miurakaigan to Shinagawa, Sengakuji)",Morning Wing,모닝윙호,#379412,1,2,0,,COMPLETE diff --git a/data/5!station_station_types.csv b/data/5!station_station_types.csv index c5875788..7b416693 100644 --- a/data/5!station_station_types.csv +++ b/data/5!station_station_types.csv @@ -3473,69 +3473,69 @@ DEFAULT,3102741,315,110,0,南が丘 DEFAULT,3102742,315,110,0,久居 DEFAULT,3102743,315,110,0,桃園 DEFAULT,3102744,315,110,0,伊勢中川 -DEFAULT,3102701,304,111,0,近鉄名古屋 -DEFAULT,3102702,304,111,1,米野 -DEFAULT,3102703,304,111,1,黄金 -DEFAULT,3102704,304,111,1,烏森 -DEFAULT,3102705,304,111,1,近鉄八田 -DEFAULT,3102706,304,111,1,伏屋 -DEFAULT,3102707,304,111,1,戸田 -DEFAULT,3102708,304,111,0,近鉄蟹江 -DEFAULT,3102709,304,111,1,富吉 -DEFAULT,3102710,304,111,1,佐古木 -DEFAULT,3102711,304,111,0,近鉄弥富 -DEFAULT,3102712,304,111,1,近鉄長島 -DEFAULT,3102713,304,111,0,桑名 -DEFAULT,3102714,304,111,1,益生 -DEFAULT,3102715,304,111,1,伊勢朝日 -DEFAULT,3102716,304,111,1,川越富洲原 -DEFAULT,3102717,304,111,0,近鉄富田 -DEFAULT,3102718,304,111,1,霞ヶ浦 -DEFAULT,3102719,304,111,1,阿倉川 -DEFAULT,3102720,304,111,1,川原町 -DEFAULT,3102721,304,111,0,近鉄四日市 -DEFAULT,3102722,304,111,1,新正 -DEFAULT,3102723,304,111,1,海山道 -DEFAULT,3102724,304,111,0,塩浜 -DEFAULT,3102725,304,111,1,北楠 -DEFAULT,3102726,304,111,1,楠 -DEFAULT,3102727,304,111,1,長太ノ浦 -DEFAULT,3102728,304,111,1,箕田 -DEFAULT,3102729,304,111,0,伊勢若松 -DEFAULT,3102730,304,111,1,千代崎 -DEFAULT,3102731,304,111,0,白子 -DEFAULT,3102732,304,111,1,鼓ヶ浦 -DEFAULT,3102733,304,111,1,磯山 -DEFAULT,3102734,304,111,1,千里 -DEFAULT,3102735,304,111,1,豊津上野 -DEFAULT,3102736,304,111,1,白塚 -DEFAULT,3102737,304,111,1,高田本山 -DEFAULT,3102738,304,111,0,江戸橋 -DEFAULT,3102739,304,111,0,津 -DEFAULT,3102740,304,111,0,津新町 -DEFAULT,3102741,304,111,0,南が丘 -DEFAULT,3102742,304,111,0,久居 -DEFAULT,3102743,304,111,0,桃園 -DEFAULT,3102744,304,111,0,伊勢中川 -DEFAULT,3100901,304,111,0,伊勢中川 -DEFAULT,3100902,304,111,1,伊勢中原 -DEFAULT,3100903,304,111,1,松ヶ崎 -DEFAULT,3100904,304,111,0,松阪 -DEFAULT,3100905,304,111,1,東松阪 -DEFAULT,3100906,304,111,1,櫛田 -DEFAULT,3100907,304,111,1,漕代 -DEFAULT,3100908,304,111,1,斎宮 -DEFAULT,3100909,304,111,1,明星 -DEFAULT,3100910,304,111,1,明野 -DEFAULT,3100911,304,111,1,小俣 -DEFAULT,3100912,304,111,1,宮町 -DEFAULT,3100913,304,111,0,伊勢市 -DEFAULT,3100914,304,111,0,宇治山田 -DEFAULT,3101001,304,111,0,宇治山田 -DEFAULT,3101002,304,111,0,五十鈴川 -DEFAULT,3101003,304,111,0,朝熊 -DEFAULT,3101004,304,111,0,池の浦 -DEFAULT,3101005,304,111,0,鳥羽 +DEFAULT,3102701,375,111,0,近鉄名古屋 +DEFAULT,3102702,375,111,1,米野 +DEFAULT,3102703,375,111,1,黄金 +DEFAULT,3102704,375,111,1,烏森 +DEFAULT,3102705,375,111,1,近鉄八田 +DEFAULT,3102706,375,111,1,伏屋 +DEFAULT,3102707,375,111,1,戸田 +DEFAULT,3102708,375,111,0,近鉄蟹江 +DEFAULT,3102709,375,111,1,富吉 +DEFAULT,3102710,375,111,1,佐古木 +DEFAULT,3102711,375,111,0,近鉄弥富 +DEFAULT,3102712,375,111,1,近鉄長島 +DEFAULT,3102713,375,111,0,桑名 +DEFAULT,3102714,375,111,1,益生 +DEFAULT,3102715,375,111,1,伊勢朝日 +DEFAULT,3102716,375,111,1,川越富洲原 +DEFAULT,3102717,375,111,0,近鉄富田 +DEFAULT,3102718,375,111,1,霞ヶ浦 +DEFAULT,3102719,375,111,1,阿倉川 +DEFAULT,3102720,375,111,1,川原町 +DEFAULT,3102721,375,111,0,近鉄四日市 +DEFAULT,3102722,375,111,1,新正 +DEFAULT,3102723,375,111,1,海山道 +DEFAULT,3102724,375,111,0,塩浜 +DEFAULT,3102725,375,111,1,北楠 +DEFAULT,3102726,375,111,1,楠 +DEFAULT,3102727,375,111,1,長太ノ浦 +DEFAULT,3102728,375,111,1,箕田 +DEFAULT,3102729,375,111,0,伊勢若松 +DEFAULT,3102730,375,111,1,千代崎 +DEFAULT,3102731,375,111,0,白子 +DEFAULT,3102732,375,111,1,鼓ヶ浦 +DEFAULT,3102733,375,111,1,磯山 +DEFAULT,3102734,375,111,1,千里 +DEFAULT,3102735,375,111,1,豊津上野 +DEFAULT,3102736,375,111,1,白塚 +DEFAULT,3102737,375,111,1,高田本山 +DEFAULT,3102738,375,111,0,江戸橋 +DEFAULT,3102739,375,111,0,津 +DEFAULT,3102740,375,111,0,津新町 +DEFAULT,3102741,375,111,0,南が丘 +DEFAULT,3102742,375,111,0,久居 +DEFAULT,3102743,375,111,0,桃園 +DEFAULT,3102744,375,111,0,伊勢中川 +DEFAULT,3100901,375,111,0,伊勢中川 +DEFAULT,3100902,375,111,1,伊勢中原 +DEFAULT,3100903,375,111,1,松ヶ崎 +DEFAULT,3100904,375,111,0,松阪 +DEFAULT,3100905,375,111,1,東松阪 +DEFAULT,3100906,375,111,1,櫛田 +DEFAULT,3100907,375,111,1,漕代 +DEFAULT,3100908,375,111,1,斎宮 +DEFAULT,3100909,375,111,1,明星 +DEFAULT,3100910,375,111,1,明野 +DEFAULT,3100911,375,111,1,小俣 +DEFAULT,3100912,375,111,1,宮町 +DEFAULT,3100913,375,111,0,伊勢市 +DEFAULT,3100914,375,111,0,宇治山田 +DEFAULT,3101001,375,111,0,宇治山田 +DEFAULT,3101002,375,111,0,五十鈴川 +DEFAULT,3101003,375,111,0,朝熊 +DEFAULT,3101004,375,111,0,池の浦 +DEFAULT,3101005,375,111,0,鳥羽 DEFAULT,3102701,306,112,0,近鉄名古屋 DEFAULT,3102702,306,112,1,米野 DEFAULT,3102703,306,112,1,黄金 @@ -12065,49 +12065,49 @@ DEFAULT,3102513,315,316,0,久津川 DEFAULT,3102514,315,316,0,寺田 DEFAULT,3102515,315,316,0,富野荘 DEFAULT,3102516,315,316,0,新田辺 -DEFAULT,3102501,304,317,0,京都 -DEFAULT,3102502,304,317,0,東寺 -DEFAULT,3102503,304,317,1,十条 -DEFAULT,3102504,304,317,1,上鳥羽口 -DEFAULT,3102505,304,317,0,竹田 -DEFAULT,3102506,304,317,1,伏見 -DEFAULT,3102507,304,317,0,近鉄丹波橋 -DEFAULT,3102508,304,317,0,桃山御陵前 -DEFAULT,3102509,304,317,1,向島 -DEFAULT,3102510,304,317,1,小倉 -DEFAULT,3102511,304,317,1,伊勢田 -DEFAULT,3102512,304,317,0,大久保 -DEFAULT,3102513,304,317,1,久津川 -DEFAULT,3102514,304,317,1,寺田 -DEFAULT,3102515,304,317,1,富野荘 -DEFAULT,3102516,304,317,0,新田辺 -DEFAULT,3102517,304,317,1,興戸 -DEFAULT,3102518,304,317,1,三山木 -DEFAULT,3102519,304,317,1,近鉄宮津 -DEFAULT,3102520,304,317,1,狛田 -DEFAULT,3102521,304,317,0,新祝園 -DEFAULT,3102522,304,317,1,木津川台 -DEFAULT,3102523,304,317,1,山田川 -DEFAULT,3102524,304,317,0,高の原 -DEFAULT,3102525,304,317,1,平城 -DEFAULT,3102526,304,317,0,大和西大寺 -DEFAULT,3100201,304,317,0,大和西大寺 -DEFAULT,3100202,304,317,1,尼ヶ辻 -DEFAULT,3100203,304,317,2,西ノ京 -DEFAULT,3100204,304,317,1,九条 -DEFAULT,3100205,304,317,0,近鉄郡山 -DEFAULT,3100206,304,317,1,筒井 -DEFAULT,3100207,304,317,0,平端 -DEFAULT,3100208,304,317,1,ファミリー公園前 -DEFAULT,3100209,304,317,1,結崎 -DEFAULT,3100210,304,317,1,石見 -DEFAULT,3100211,304,317,0,田原本 -DEFAULT,3100212,304,317,1,笠縫 -DEFAULT,3100213,304,317,1,新ノ口 -DEFAULT,3100214,304,317,0,大和八木 -DEFAULT,3100215,304,317,0,八木西口 -DEFAULT,3100216,304,317,0,畝傍御陵前 -DEFAULT,3100217,304,317,0,橿原神宮前 +DEFAULT,3102501,375,317,0,京都 +DEFAULT,3102502,375,317,0,東寺 +DEFAULT,3102503,375,317,1,十条 +DEFAULT,3102504,375,317,1,上鳥羽口 +DEFAULT,3102505,375,317,0,竹田 +DEFAULT,3102506,375,317,1,伏見 +DEFAULT,3102507,375,317,0,近鉄丹波橋 +DEFAULT,3102508,375,317,0,桃山御陵前 +DEFAULT,3102509,375,317,1,向島 +DEFAULT,3102510,375,317,1,小倉 +DEFAULT,3102511,375,317,1,伊勢田 +DEFAULT,3102512,375,317,0,大久保 +DEFAULT,3102513,375,317,1,久津川 +DEFAULT,3102514,375,317,1,寺田 +DEFAULT,3102515,375,317,1,富野荘 +DEFAULT,3102516,375,317,0,新田辺 +DEFAULT,3102517,375,317,1,興戸 +DEFAULT,3102518,375,317,1,三山木 +DEFAULT,3102519,375,317,1,近鉄宮津 +DEFAULT,3102520,375,317,1,狛田 +DEFAULT,3102521,375,317,0,新祝園 +DEFAULT,3102522,375,317,1,木津川台 +DEFAULT,3102523,375,317,1,山田川 +DEFAULT,3102524,375,317,0,高の原 +DEFAULT,3102525,375,317,1,平城 +DEFAULT,3102526,375,317,0,大和西大寺 +DEFAULT,3100201,375,317,0,大和西大寺 +DEFAULT,3100202,375,317,1,尼ヶ辻 +DEFAULT,3100203,375,317,2,西ノ京 +DEFAULT,3100204,375,317,1,九条 +DEFAULT,3100205,375,317,0,近鉄郡山 +DEFAULT,3100206,375,317,1,筒井 +DEFAULT,3100207,375,317,0,平端 +DEFAULT,3100208,375,317,1,ファミリー公園前 +DEFAULT,3100209,375,317,1,結崎 +DEFAULT,3100210,375,317,1,石見 +DEFAULT,3100211,375,317,0,田原本 +DEFAULT,3100212,375,317,1,笠縫 +DEFAULT,3100213,375,317,1,新ノ口 +DEFAULT,3100214,375,317,0,大和八木 +DEFAULT,3100215,375,317,0,八木西口 +DEFAULT,3100216,375,317,0,畝傍御陵前 +DEFAULT,3100217,375,317,0,橿原神宮前 DEFAULT,3102501,306,318,0,京都 DEFAULT,3102502,306,318,1,東寺 DEFAULT,3102503,306,318,1,十条 @@ -12179,34 +12179,34 @@ DEFAULT,3102525,100,319,0,平城 DEFAULT,3102526,100,319,0,大和西大寺 DEFAULT,3102527,100,319,0,新大宮 DEFAULT,3102528,100,319,0,近鉄奈良 -DEFAULT,3102501,304,320,0,京都 -DEFAULT,3102502,304,320,0,東寺 -DEFAULT,3102503,304,320,1,十条 -DEFAULT,3102504,304,320,1,上鳥羽口 -DEFAULT,3102505,304,320,0,竹田 -DEFAULT,3102506,304,320,1,伏見 -DEFAULT,3102507,304,320,0,近鉄丹波橋 -DEFAULT,3102508,304,320,0,桃山御陵前 -DEFAULT,3102509,304,320,1,向島 -DEFAULT,3102510,304,320,1,小倉 -DEFAULT,3102511,304,320,1,伊勢田 -DEFAULT,3102512,304,320,0,大久保 -DEFAULT,3102513,304,320,1,久津川 -DEFAULT,3102514,304,320,1,寺田 -DEFAULT,3102515,304,320,1,富野荘 -DEFAULT,3102516,304,320,0,新田辺 -DEFAULT,3102517,304,320,1,興戸 -DEFAULT,3102518,304,320,1,三山木 -DEFAULT,3102519,304,320,1,近鉄宮津 -DEFAULT,3102520,304,320,1,狛田 -DEFAULT,3102521,304,320,0,新祝園 -DEFAULT,3102522,304,320,1,木津川台 -DEFAULT,3102523,304,320,1,山田川 -DEFAULT,3102524,304,320,0,高の原 -DEFAULT,3102525,304,320,1,平城 -DEFAULT,3102526,304,320,0,大和西大寺 -DEFAULT,3102527,304,320,0,新大宮 -DEFAULT,3102528,304,320,0,近鉄奈良 +DEFAULT,3102501,375,320,0,京都 +DEFAULT,3102502,375,320,0,東寺 +DEFAULT,3102503,375,320,1,十条 +DEFAULT,3102504,375,320,1,上鳥羽口 +DEFAULT,3102505,375,320,0,竹田 +DEFAULT,3102506,375,320,1,伏見 +DEFAULT,3102507,375,320,0,近鉄丹波橋 +DEFAULT,3102508,375,320,0,桃山御陵前 +DEFAULT,3102509,375,320,1,向島 +DEFAULT,3102510,375,320,1,小倉 +DEFAULT,3102511,375,320,1,伊勢田 +DEFAULT,3102512,375,320,0,大久保 +DEFAULT,3102513,375,320,1,久津川 +DEFAULT,3102514,375,320,1,寺田 +DEFAULT,3102515,375,320,1,富野荘 +DEFAULT,3102516,375,320,0,新田辺 +DEFAULT,3102517,375,320,1,興戸 +DEFAULT,3102518,375,320,1,三山木 +DEFAULT,3102519,375,320,1,近鉄宮津 +DEFAULT,3102520,375,320,1,狛田 +DEFAULT,3102521,375,320,0,新祝園 +DEFAULT,3102522,375,320,1,木津川台 +DEFAULT,3102523,375,320,1,山田川 +DEFAULT,3102524,375,320,0,高の原 +DEFAULT,3102525,375,320,1,平城 +DEFAULT,3102526,375,320,0,大和西大寺 +DEFAULT,3102527,375,320,0,新大宮 +DEFAULT,3102528,375,320,0,近鉄奈良 DEFAULT,3102501,306,321,0,京都 DEFAULT,3102502,306,321,1,東寺 DEFAULT,3102503,306,321,1,十条 @@ -12272,43 +12272,43 @@ DEFAULT,3101101,100,322,0,平端 DEFAULT,3101102,100,322,0,二階堂 DEFAULT,3101103,100,322,0,前栽 DEFAULT,3101104,100,322,0,天理 -DEFAULT,3102501,304,323,0,京都 -DEFAULT,3102502,304,323,0,東寺 -DEFAULT,3102503,304,323,1,十条 -DEFAULT,3102504,304,323,1,上鳥羽口 -DEFAULT,3102505,304,323,0,竹田 -DEFAULT,3102506,304,323,1,伏見 -DEFAULT,3102507,304,323,0,近鉄丹波橋 -DEFAULT,3102508,304,323,0,桃山御陵前 -DEFAULT,3102509,304,323,1,向島 -DEFAULT,3102510,304,323,1,小倉 -DEFAULT,3102511,304,323,1,伊勢田 -DEFAULT,3102512,304,323,0,大久保 -DEFAULT,3102513,304,323,1,久津川 -DEFAULT,3102514,304,323,1,寺田 -DEFAULT,3102515,304,323,1,富野荘 -DEFAULT,3102516,304,323,0,新田辺 -DEFAULT,3102517,304,323,1,興戸 -DEFAULT,3102518,304,323,1,三山木 -DEFAULT,3102519,304,323,1,近鉄宮津 -DEFAULT,3102520,304,323,1,狛田 -DEFAULT,3102521,304,323,0,新祝園 -DEFAULT,3102522,304,323,1,木津川台 -DEFAULT,3102523,304,323,1,山田川 -DEFAULT,3102524,304,323,0,高の原 -DEFAULT,3102525,304,323,1,平城 -DEFAULT,3102526,304,323,0,大和西大寺 -DEFAULT,3100201,304,323,0,大和西大寺 -DEFAULT,3100202,304,323,1,尼ヶ辻 -DEFAULT,3100203,304,323,2,西ノ京 -DEFAULT,3100204,304,323,1,九条 -DEFAULT,3100205,304,323,0,近鉄郡山 -DEFAULT,3100206,304,323,1,筒井 -DEFAULT,3100207,304,323,0,平端 -DEFAULT,3101101,304,323,0,平端 -DEFAULT,3101102,304,323,0,二階堂 -DEFAULT,3101103,304,323,0,前栽 -DEFAULT,3101104,304,323,0,天理 +DEFAULT,3102501,375,323,0,京都 +DEFAULT,3102502,375,323,0,東寺 +DEFAULT,3102503,375,323,1,十条 +DEFAULT,3102504,375,323,1,上鳥羽口 +DEFAULT,3102505,375,323,0,竹田 +DEFAULT,3102506,375,323,1,伏見 +DEFAULT,3102507,375,323,0,近鉄丹波橋 +DEFAULT,3102508,375,323,0,桃山御陵前 +DEFAULT,3102509,375,323,1,向島 +DEFAULT,3102510,375,323,1,小倉 +DEFAULT,3102511,375,323,1,伊勢田 +DEFAULT,3102512,375,323,0,大久保 +DEFAULT,3102513,375,323,1,久津川 +DEFAULT,3102514,375,323,1,寺田 +DEFAULT,3102515,375,323,1,富野荘 +DEFAULT,3102516,375,323,0,新田辺 +DEFAULT,3102517,375,323,1,興戸 +DEFAULT,3102518,375,323,1,三山木 +DEFAULT,3102519,375,323,1,近鉄宮津 +DEFAULT,3102520,375,323,1,狛田 +DEFAULT,3102521,375,323,0,新祝園 +DEFAULT,3102522,375,323,1,木津川台 +DEFAULT,3102523,375,323,1,山田川 +DEFAULT,3102524,375,323,0,高の原 +DEFAULT,3102525,375,323,1,平城 +DEFAULT,3102526,375,323,0,大和西大寺 +DEFAULT,3100201,375,323,0,大和西大寺 +DEFAULT,3100202,375,323,1,尼ヶ辻 +DEFAULT,3100203,375,323,2,西ノ京 +DEFAULT,3100204,375,323,1,九条 +DEFAULT,3100205,375,323,0,近鉄郡山 +DEFAULT,3100206,375,323,1,筒井 +DEFAULT,3100207,375,323,0,平端 +DEFAULT,3101101,375,323,0,平端 +DEFAULT,3101102,375,323,0,二階堂 +DEFAULT,3101103,375,323,0,前栽 +DEFAULT,3101104,375,323,0,天理 DEFAULT,3100301,100,324,0,大阪阿部野橋 DEFAULT,3100302,100,324,0,河堀口 DEFAULT,3100303,100,324,0,北田辺 @@ -12381,78 +12381,78 @@ DEFAULT,3100325,315,325,0,浮孔 DEFAULT,3100326,315,325,0,坊城 DEFAULT,3100327,315,325,0,橿原神宮西口 DEFAULT,3100328,315,325,0,橿原神宮前 -DEFAULT,3100301,319,326,0,大阪阿部野橋 -DEFAULT,3100302,319,326,1,河堀口 -DEFAULT,3100303,319,326,1,北田辺 -DEFAULT,3100304,319,326,1,今川 -DEFAULT,3100305,319,326,1,針中野 -DEFAULT,3100306,319,326,1,矢田 -DEFAULT,3100307,319,326,1,河内天美 -DEFAULT,3100308,319,326,1,布忍 -DEFAULT,3100309,319,326,1,高見ノ里 -DEFAULT,3100310,319,326,1,河内松原 -DEFAULT,3100311,319,326,1,恵我ノ荘 -DEFAULT,3100312,319,326,1,高鷲 -DEFAULT,3100313,319,326,1,藤井寺 -DEFAULT,3100314,319,326,1,土師ノ里 -DEFAULT,3100315,319,326,1,道明寺 -DEFAULT,3100316,319,326,0,古市 -DEFAULT,3100317,319,326,1,駒ヶ谷 -DEFAULT,3100318,319,326,1,上ノ太子 -DEFAULT,3100319,319,326,1,二上山 -DEFAULT,3100320,319,326,1,二上神社口 -DEFAULT,3100321,319,326,1,当麻寺 -DEFAULT,3100322,319,326,1,磐城 -DEFAULT,3100323,319,326,0,尺土 -DEFAULT,3100324,319,326,0,高田市 -DEFAULT,3100325,319,326,0,浮孔 -DEFAULT,3100326,319,326,0,坊城 -DEFAULT,3100327,319,326,0,橿原神宮西口 -DEFAULT,3100328,319,326,0,橿原神宮前 -DEFAULT,3100301,304,327,0,大阪阿部野橋 -DEFAULT,3100302,304,327,1,河堀口 -DEFAULT,3100303,304,327,1,北田辺 -DEFAULT,3100304,304,327,1,今川 -DEFAULT,3100305,304,327,1,針中野 -DEFAULT,3100306,304,327,1,矢田 -DEFAULT,3100307,304,327,1,河内天美 -DEFAULT,3100308,304,327,1,布忍 -DEFAULT,3100309,304,327,1,高見ノ里 -DEFAULT,3100310,304,327,1,河内松原 -DEFAULT,3100311,304,327,1,恵我ノ荘 -DEFAULT,3100312,304,327,1,高鷲 -DEFAULT,3100313,304,327,1,藤井寺 -DEFAULT,3100314,304,327,1,土師ノ里 -DEFAULT,3100315,304,327,1,道明寺 -DEFAULT,3100316,304,327,0,古市 -DEFAULT,3100317,304,327,1,駒ヶ谷 -DEFAULT,3100318,304,327,1,上ノ太子 -DEFAULT,3100319,304,327,1,二上山 -DEFAULT,3100320,304,327,1,二上神社口 -DEFAULT,3100321,304,327,1,当麻寺 -DEFAULT,3100322,304,327,1,磐城 -DEFAULT,3100323,304,327,0,尺土 -DEFAULT,3100324,304,327,0,高田市 -DEFAULT,3100325,304,327,1,浮孔 -DEFAULT,3100326,304,327,1,坊城 -DEFAULT,3100327,304,327,1,橿原神宮西口 -DEFAULT,3100328,304,327,0,橿原神宮前 -DEFAULT,3100701,304,327,0,橿原神宮前 -DEFAULT,3100702,304,327,0,岡寺 -DEFAULT,3100703,304,327,0,飛鳥 -DEFAULT,3100704,304,327,0,壺阪山 -DEFAULT,3100705,304,327,0,市尾 -DEFAULT,3100706,304,327,0,葛 -DEFAULT,3100707,304,327,0,吉野口 -DEFAULT,3100708,304,327,0,薬水 -DEFAULT,3100709,304,327,0,福神 -DEFAULT,3100710,304,327,0,大阿太 -DEFAULT,3100711,304,327,0,下市口 -DEFAULT,3100712,304,327,0,越部 -DEFAULT,3100713,304,327,0,六田 -DEFAULT,3100714,304,327,0,大和上市 -DEFAULT,3100715,304,327,0,吉野神宮 -DEFAULT,3100716,304,327,0,吉野 +DEFAULT,3100301,374,326,0,大阪阿部野橋 +DEFAULT,3100302,374,326,1,河堀口 +DEFAULT,3100303,374,326,1,北田辺 +DEFAULT,3100304,374,326,1,今川 +DEFAULT,3100305,374,326,1,針中野 +DEFAULT,3100306,374,326,1,矢田 +DEFAULT,3100307,374,326,1,河内天美 +DEFAULT,3100308,374,326,1,布忍 +DEFAULT,3100309,374,326,1,高見ノ里 +DEFAULT,3100310,374,326,1,河内松原 +DEFAULT,3100311,374,326,1,恵我ノ荘 +DEFAULT,3100312,374,326,1,高鷲 +DEFAULT,3100313,374,326,1,藤井寺 +DEFAULT,3100314,374,326,1,土師ノ里 +DEFAULT,3100315,374,326,1,道明寺 +DEFAULT,3100316,374,326,0,古市 +DEFAULT,3100317,374,326,1,駒ヶ谷 +DEFAULT,3100318,374,326,1,上ノ太子 +DEFAULT,3100374,374,326,1,二上山 +DEFAULT,3100320,374,326,1,二上神社口 +DEFAULT,3100321,374,326,1,当麻寺 +DEFAULT,3100322,374,326,1,磐城 +DEFAULT,3100323,374,326,0,尺土 +DEFAULT,3100324,374,326,0,高田市 +DEFAULT,3100325,374,326,0,浮孔 +DEFAULT,3100326,374,326,0,坊城 +DEFAULT,3100327,374,326,0,橿原神宮西口 +DEFAULT,3100328,374,326,0,橿原神宮前 +DEFAULT,3100301,375,327,0,大阪阿部野橋 +DEFAULT,3100302,375,327,1,河堀口 +DEFAULT,3100303,375,327,1,北田辺 +DEFAULT,3100304,375,327,1,今川 +DEFAULT,3100305,375,327,1,針中野 +DEFAULT,3100306,375,327,1,矢田 +DEFAULT,3100307,375,327,1,河内天美 +DEFAULT,3100308,375,327,1,布忍 +DEFAULT,3100309,375,327,1,高見ノ里 +DEFAULT,3100310,375,327,1,河内松原 +DEFAULT,3100311,375,327,1,恵我ノ荘 +DEFAULT,3100312,375,327,1,高鷲 +DEFAULT,3100313,375,327,1,藤井寺 +DEFAULT,3100314,375,327,1,土師ノ里 +DEFAULT,3100315,375,327,1,道明寺 +DEFAULT,3100316,375,327,0,古市 +DEFAULT,3100317,375,327,1,駒ヶ谷 +DEFAULT,3100318,375,327,1,上ノ太子 +DEFAULT,3100319,375,327,1,二上山 +DEFAULT,3100320,375,327,1,二上神社口 +DEFAULT,3100321,375,327,1,当麻寺 +DEFAULT,3100322,375,327,1,磐城 +DEFAULT,3100323,375,327,0,尺土 +DEFAULT,3100324,375,327,0,高田市 +DEFAULT,3100325,375,327,1,浮孔 +DEFAULT,3100326,375,327,1,坊城 +DEFAULT,3100327,375,327,1,橿原神宮西口 +DEFAULT,3100328,375,327,0,橿原神宮前 +DEFAULT,3100701,375,327,0,橿原神宮前 +DEFAULT,3100702,375,327,0,岡寺 +DEFAULT,3100703,375,327,0,飛鳥 +DEFAULT,3100704,375,327,0,壺阪山 +DEFAULT,3100705,375,327,0,市尾 +DEFAULT,3100706,375,327,0,葛 +DEFAULT,3100707,375,327,0,吉野口 +DEFAULT,3100708,375,327,0,薬水 +DEFAULT,3100709,375,327,0,福神 +DEFAULT,3100710,375,327,0,大阿太 +DEFAULT,3100711,375,327,0,下市口 +DEFAULT,3100712,375,327,0,越部 +DEFAULT,3100713,375,327,0,六田 +DEFAULT,3100714,375,327,0,大和上市 +DEFAULT,3100715,375,327,0,吉野神宮 +DEFAULT,3100716,375,327,0,吉野 DEFAULT,3100301,306,328,0,大阪阿部野橋 DEFAULT,3100302,306,328,1,河堀口 DEFAULT,3100303,306,328,1,北田辺 @@ -12545,30 +12545,30 @@ DEFAULT,3102205,315,330,0,川西 DEFAULT,3102206,315,330,0,滝谷不動 DEFAULT,3102207,315,330,0,汐ノ宮 DEFAULT,3102208,315,330,0,河内長野 -DEFAULT,3100301,304,331,0,大阪阿部野橋 -DEFAULT,3100302,304,331,1,河堀口 -DEFAULT,3100303,304,331,1,北田辺 -DEFAULT,3100304,304,331,1,今川 -DEFAULT,3100305,304,331,1,針中野 -DEFAULT,3100306,304,331,1,矢田 -DEFAULT,3100307,304,331,1,河内天美 -DEFAULT,3100308,304,331,1,布忍 -DEFAULT,3100309,304,331,1,高見ノ里 -DEFAULT,3100310,304,331,1,河内松原 -DEFAULT,3100311,304,331,1,恵我ノ荘 -DEFAULT,3100312,304,331,1,高鷲 -DEFAULT,3100313,304,331,1,藤井寺 -DEFAULT,3100314,304,331,1,土師ノ里 -DEFAULT,3100315,304,331,1,道明寺 -DEFAULT,3100316,304,331,0,古市 -DEFAULT,3102201,304,331,0,古市 -DEFAULT,3102202,304,331,0,喜志 -DEFAULT,3102203,304,331,0,富田林 -DEFAULT,3102204,304,331,0,富田林西口 -DEFAULT,3102205,304,331,0,川西 -DEFAULT,3102206,304,331,0,滝谷不動 -DEFAULT,3102207,304,331,0,汐ノ宮 -DEFAULT,3102208,304,331,0,河内長野 +DEFAULT,3100301,375,331,0,大阪阿部野橋 +DEFAULT,3100302,375,331,1,河堀口 +DEFAULT,3100303,375,331,1,北田辺 +DEFAULT,3100304,375,331,1,今川 +DEFAULT,3100305,375,331,1,針中野 +DEFAULT,3100306,375,331,1,矢田 +DEFAULT,3100307,375,331,1,河内天美 +DEFAULT,3100308,375,331,1,布忍 +DEFAULT,3100309,375,331,1,高見ノ里 +DEFAULT,3100310,375,331,1,河内松原 +DEFAULT,3100311,375,331,1,恵我ノ荘 +DEFAULT,3100312,375,331,1,高鷲 +DEFAULT,3100313,375,331,1,藤井寺 +DEFAULT,3100314,375,331,1,土師ノ里 +DEFAULT,3100315,375,331,1,道明寺 +DEFAULT,3100316,375,331,0,古市 +DEFAULT,3102201,375,331,0,古市 +DEFAULT,3102202,375,331,0,喜志 +DEFAULT,3102203,375,331,0,富田林 +DEFAULT,3102204,375,331,0,富田林西口 +DEFAULT,3102205,375,331,0,川西 +DEFAULT,3102206,375,331,0,滝谷不動 +DEFAULT,3102207,375,331,0,汐ノ宮 +DEFAULT,3102208,375,331,0,河内長野 DEFAULT,3500207,373,332,0,尼崎 DEFAULT,3500206,373,332,0,大物 DEFAULT,3500205,373,332,0,出来島 @@ -12641,91 +12641,91 @@ DEFAULT,3102016,315,333,0,菖蒲池 DEFAULT,3102017,315,333,0,大和西大寺 DEFAULT,3102018,315,333,0,新大宮 DEFAULT,3102019,315,333,0,近鉄奈良 -DEFAULT,3100101,304,334,0,大阪難波 -DEFAULT,3100102,304,334,0,近鉄日本橋 -DEFAULT,3100103,304,334,0,大阪上本町 -DEFAULT,3102020,304,334,0,大阪上本町 -DEFAULT,3102021,304,334,0,鶴橋 -DEFAULT,3102022,304,334,1,今里 -DEFAULT,3102001,304,334,0,布施 -DEFAULT,3102002,304,334,1,河内永和 -DEFAULT,3102003,304,334,1,河内小阪 -DEFAULT,3102004,304,334,1,八戸ノ里 -DEFAULT,3102005,304,334,1,若江岩田 -DEFAULT,3102006,304,334,1,河内花園 -DEFAULT,3102007,304,334,1,東花園 -DEFAULT,3102008,304,334,1,瓢箪山 -DEFAULT,3102009,304,334,1,枚岡 -DEFAULT,3102010,304,334,1,額田 -DEFAULT,3102011,304,334,0,石切 -DEFAULT,3102012,304,334,0,生駒 -DEFAULT,3102013,304,334,1,東生駒 -DEFAULT,3102014,304,334,1,富雄 -DEFAULT,3102015,304,334,0,学園前 -DEFAULT,3102016,304,334,1,菖蒲池 -DEFAULT,3102017,304,334,0,大和西大寺 -DEFAULT,3102018,304,334,0,新大宮 -DEFAULT,3102019,304,334,0,近鉄奈良 -DEFAULT,3500132,330,335,0,神戸三宮 -DEFAULT,3500131,330,335,1,春日野道 -DEFAULT,3500130,330,335,1,岩屋 -DEFAULT,3500129,330,335,1,西灘 -DEFAULT,3500128,330,335,1,大石 -DEFAULT,3500127,330,335,1,新在家 -DEFAULT,3500126,330,335,1,石屋川 -DEFAULT,3500125,330,335,1,御影 -DEFAULT,3500124,330,335,1,住吉 -DEFAULT,3500123,330,335,0,魚崎 -DEFAULT,3500122,330,335,1,青木 -DEFAULT,3500121,330,335,1,深江 -DEFAULT,3500120,330,335,2,芦屋 -DEFAULT,3500119,330,335,1,打出 -DEFAULT,3500118,330,335,1,香櫨園 -DEFAULT,3500117,330,335,0,西宮 -DEFAULT,3500116,330,335,2,今津 -DEFAULT,3500115,330,335,1,久寿川 -DEFAULT,3500114,330,335,0,甲子園 -DEFAULT,3500113,330,335,1,鳴尾・武庫川女子大前 -DEFAULT,3500112,330,335,2,武庫川 -DEFAULT,3500111,330,335,1,尼崎センタープール前 -DEFAULT,3500110,330,335,1,出屋敷 -DEFAULT,3500109,330,335,0,尼崎 -DEFAULT,3500207,330,335,0,尼崎 -DEFAULT,3500206,330,335,1,大物 -DEFAULT,3500205,330,335,1,出来島 -DEFAULT,3500204,330,335,1,福 -DEFAULT,3500203,330,335,1,伝法 -DEFAULT,3500202,330,335,1,千鳥橋 -DEFAULT,3500201,330,335,0,西九条 -DEFAULT,3500208,330,335,0,九条 -DEFAULT,3500209,330,335,0,ドーム前 -DEFAULT,3500210,330,335,0,桜川 -DEFAULT,3500211,330,335,0,大阪難波 -DEFAULT,3100101,330,335,0,大阪難波 -DEFAULT,3100102,330,335,0,近鉄日本橋 -DEFAULT,3100103,330,335,0,大阪上本町 -DEFAULT,3102020,330,335,0,大阪上本町 -DEFAULT,3102021,330,335,0,鶴橋 -DEFAULT,3102022,330,335,1,今里 -DEFAULT,3102001,330,335,1,布施 -DEFAULT,3102002,330,335,1,河内永和 -DEFAULT,3102003,330,335,1,河内小阪 -DEFAULT,3102004,330,335,1,八戸ノ里 -DEFAULT,3102005,330,335,1,若江岩田 -DEFAULT,3102006,330,335,1,河内花園 -DEFAULT,3102007,330,335,1,東花園 -DEFAULT,3102008,330,335,1,瓢箪山 -DEFAULT,3102009,330,335,1,枚岡 -DEFAULT,3102010,330,335,1,額田 -DEFAULT,3102011,330,335,1,石切 -DEFAULT,3102012,330,335,0,生駒 -DEFAULT,3102013,330,335,1,東生駒 -DEFAULT,3102014,330,335,1,富雄 -DEFAULT,3102015,330,335,0,学園前 -DEFAULT,3102016,330,335,1,菖蒲池 -DEFAULT,3102017,330,335,0,大和西大寺 -DEFAULT,3102018,330,335,0,新大宮 -DEFAULT,3102019,330,335,0,近鉄奈良 +DEFAULT,3100101,375,334,0,大阪難波 +DEFAULT,3100102,375,334,0,近鉄日本橋 +DEFAULT,3100103,375,334,0,大阪上本町 +DEFAULT,3102020,375,334,0,大阪上本町 +DEFAULT,3102021,375,334,0,鶴橋 +DEFAULT,3102022,375,334,1,今里 +DEFAULT,3102001,375,334,0,布施 +DEFAULT,3102002,375,334,1,河内永和 +DEFAULT,3102003,375,334,1,河内小阪 +DEFAULT,3102004,375,334,1,八戸ノ里 +DEFAULT,3102005,375,334,1,若江岩田 +DEFAULT,3102006,375,334,1,河内花園 +DEFAULT,3102007,375,334,1,東花園 +DEFAULT,3102008,375,334,1,瓢箪山 +DEFAULT,3102009,375,334,1,枚岡 +DEFAULT,3102010,375,334,1,額田 +DEFAULT,3102011,375,334,0,石切 +DEFAULT,3102012,375,334,0,生駒 +DEFAULT,3102013,375,334,1,東生駒 +DEFAULT,3102014,375,334,1,富雄 +DEFAULT,3102015,375,334,0,学園前 +DEFAULT,3102016,375,334,1,菖蒲池 +DEFAULT,3102017,375,334,0,大和西大寺 +DEFAULT,3102018,375,334,0,新大宮 +DEFAULT,3102019,375,334,0,近鉄奈良 +DEFAULT,3500132,377,335,0,神戸三宮 +DEFAULT,3500131,377,335,1,春日野道 +DEFAULT,3500130,377,335,1,岩屋 +DEFAULT,3500129,377,335,1,西灘 +DEFAULT,3500128,377,335,1,大石 +DEFAULT,3500127,377,335,1,新在家 +DEFAULT,3500126,377,335,1,石屋川 +DEFAULT,3500125,377,335,1,御影 +DEFAULT,3500124,377,335,1,住吉 +DEFAULT,3500123,377,335,0,魚崎 +DEFAULT,3500122,377,335,1,青木 +DEFAULT,3500121,377,335,1,深江 +DEFAULT,3500120,377,335,2,芦屋 +DEFAULT,3500119,377,335,1,打出 +DEFAULT,3500118,377,335,1,香櫨園 +DEFAULT,3500117,377,335,0,西宮 +DEFAULT,3500116,377,335,2,今津 +DEFAULT,3500115,377,335,1,久寿川 +DEFAULT,3500114,377,335,0,甲子園 +DEFAULT,3500113,377,335,1,鳴尾・武庫川女子大前 +DEFAULT,3500112,377,335,2,武庫川 +DEFAULT,3500111,377,335,1,尼崎センタープール前 +DEFAULT,3500110,377,335,1,出屋敷 +DEFAULT,3500109,377,335,0,尼崎 +DEFAULT,3500207,377,335,0,尼崎 +DEFAULT,3500206,377,335,1,大物 +DEFAULT,3500205,377,335,1,出来島 +DEFAULT,3500204,377,335,1,福 +DEFAULT,3500203,377,335,1,伝法 +DEFAULT,3500202,377,335,1,千鳥橋 +DEFAULT,3500201,377,335,0,西九条 +DEFAULT,3500208,377,335,0,九条 +DEFAULT,3500209,377,335,0,ドーム前 +DEFAULT,3500210,377,335,0,桜川 +DEFAULT,3500211,377,335,0,大阪難波 +DEFAULT,3100101,376,335,0,大阪難波 +DEFAULT,3100102,376,335,0,近鉄日本橋 +DEFAULT,3100103,376,335,0,大阪上本町 +DEFAULT,3102020,376,335,0,大阪上本町 +DEFAULT,3102021,376,335,0,鶴橋 +DEFAULT,3102022,376,335,1,今里 +DEFAULT,3102001,376,335,1,布施 +DEFAULT,3102002,376,335,1,河内永和 +DEFAULT,3102003,376,335,1,河内小阪 +DEFAULT,3102004,376,335,1,八戸ノ里 +DEFAULT,3102005,376,335,1,若江岩田 +DEFAULT,3102006,376,335,1,河内花園 +DEFAULT,3102007,376,335,1,東花園 +DEFAULT,3102008,376,335,1,瓢箪山 +DEFAULT,3102009,376,335,1,枚岡 +DEFAULT,3102010,376,335,1,額田 +DEFAULT,3102011,376,335,1,石切 +DEFAULT,3102012,376,335,0,生駒 +DEFAULT,3102013,376,335,1,東生駒 +DEFAULT,3102014,376,335,1,富雄 +DEFAULT,3102015,376,335,0,学園前 +DEFAULT,3102016,376,335,1,菖蒲池 +DEFAULT,3102017,376,335,0,大和西大寺 +DEFAULT,3102018,376,335,0,新大宮 +DEFAULT,3102019,376,335,0,近鉄奈良 DEFAULT,3100101,306,336,0,大阪難波 DEFAULT,3100102,306,336,1,近鉄日本橋 DEFAULT,3100103,306,336,0,大阪上本町 @@ -12871,137 +12871,137 @@ DEFAULT,3100533,315,339,0,室生口大野 DEFAULT,3100534,315,339,0,三本松 DEFAULT,3100535,315,339,0,赤目口 DEFAULT,3100536,315,339,0,名張 -DEFAULT,3100501,304,340,0,大阪上本町 -DEFAULT,3100502,304,340,0,鶴橋 -DEFAULT,3100503,304,340,1,今里 -DEFAULT,3100504,304,340,0,布施 -DEFAULT,3100505,304,340,1,俊徳道 -DEFAULT,3100506,304,340,1,長瀬 -DEFAULT,3100507,304,340,1,弥刀 -DEFAULT,3100508,304,340,1,久宝寺口 -DEFAULT,3100509,304,340,1,近鉄八尾 -DEFAULT,3100510,304,340,1,河内山本 -DEFAULT,3100511,304,340,1,高安 -DEFAULT,3100512,304,340,1,恩智 -DEFAULT,3100513,304,340,1,法善寺 -DEFAULT,3100514,304,340,1,堅下 -DEFAULT,3100515,304,340,1,安堂 -DEFAULT,3100516,304,340,0,河内国分 -DEFAULT,3100517,304,340,1,大阪教育大前 -DEFAULT,3100518,304,340,1,関屋 -DEFAULT,3100519,304,340,1,二上 -DEFAULT,3100520,304,340,1,近鉄下田 -DEFAULT,3100521,304,340,0,五位堂 -DEFAULT,3100522,304,340,1,築山 -DEFAULT,3100523,304,340,0,大和高田 -DEFAULT,3100524,304,340,1,松塚 -DEFAULT,3100525,304,340,1,真菅 -DEFAULT,3100526,304,340,0,大和八木 -DEFAULT,3100527,304,340,1,耳成 -DEFAULT,3100528,304,340,1,大福 -DEFAULT,3100529,304,340,0,桜井 -DEFAULT,3100530,304,340,0,大和朝倉 -DEFAULT,3100531,304,340,0,長谷寺 -DEFAULT,3100532,304,340,0,榛原 -DEFAULT,3100533,304,340,0,室生口大野 -DEFAULT,3100534,304,340,0,三本松 -DEFAULT,3100535,304,340,0,赤目口 -DEFAULT,3100536,304,340,0,名張 -DEFAULT,3100537,304,340,0,桔梗が丘 -DEFAULT,3100538,304,340,0,美旗 -DEFAULT,3100539,304,340,0,伊賀神戸 -DEFAULT,3100540,304,340,0,青山町 -DEFAULT,3100541,304,340,0,伊賀上津 -DEFAULT,3100542,304,340,0,西青山 -DEFAULT,3100543,304,340,0,東青山 -DEFAULT,3100544,304,340,0,榊原温泉口 -DEFAULT,3100545,304,340,1,大三 -DEFAULT,3100546,304,340,1,伊勢石橋 -DEFAULT,3100547,304,340,1,川合高岡 -DEFAULT,3100548,304,340,0,伊勢中川 -DEFAULT,3100901,304,340,0,伊勢中川 -DEFAULT,3100902,304,340,1,伊勢中原 -DEFAULT,3100903,304,340,1,松ヶ崎 -DEFAULT,3100904,304,340,0,松阪 -DEFAULT,3100905,304,340,1,東松阪 -DEFAULT,3100906,304,340,1,櫛田 -DEFAULT,3100907,304,340,1,漕代 -DEFAULT,3100908,304,340,1,斎宮 -DEFAULT,3100909,304,340,1,明星 -DEFAULT,3100910,304,340,1,明野 -DEFAULT,3100911,304,340,1,小俣 -DEFAULT,3100912,304,340,1,宮町 -DEFAULT,3100913,304,340,0,伊勢市 -DEFAULT,3100914,304,340,0,宇治山田 -DEFAULT,3101001,304,340,0,宇治山田 -DEFAULT,3101002,304,340,0,五十鈴川 -DEFAULT,3100501,330,341,0,大阪上本町 -DEFAULT,3100502,330,341,0,鶴橋 -DEFAULT,3100503,330,341,1,今里 -DEFAULT,3100504,330,341,1,布施 -DEFAULT,3100505,330,341,1,俊徳道 -DEFAULT,3100506,330,341,1,長瀬 -DEFAULT,3100507,330,341,1,弥刀 -DEFAULT,3100508,330,341,1,久宝寺口 -DEFAULT,3100509,330,341,1,近鉄八尾 -DEFAULT,3100510,330,341,1,河内山本 -DEFAULT,3100511,330,341,1,高安 -DEFAULT,3100512,330,341,1,恩智 -DEFAULT,3100513,330,341,1,法善寺 -DEFAULT,3100514,330,341,1,堅下 -DEFAULT,3100515,330,341,1,安堂 -DEFAULT,3100516,330,341,1,河内国分 -DEFAULT,3100517,330,341,1,大阪教育大前 -DEFAULT,3100518,330,341,1,関屋 -DEFAULT,3100519,330,341,1,二上 -DEFAULT,3100520,330,341,1,近鉄下田 -DEFAULT,3100521,330,341,0,五位堂 -DEFAULT,3100522,330,341,1,築山 -DEFAULT,3100523,330,341,0,大和高田 -DEFAULT,3100524,330,341,1,松塚 -DEFAULT,3100525,330,341,1,真菅 -DEFAULT,3100526,330,341,0,大和八木 -DEFAULT,3100527,330,341,1,耳成 -DEFAULT,3100528,330,341,1,大福 -DEFAULT,3100529,330,341,0,桜井 -DEFAULT,3100530,330,341,1,大和朝倉 -DEFAULT,3100531,330,341,1,長谷寺 -DEFAULT,3100532,330,341,0,榛原 -DEFAULT,3100533,330,341,0,室生口大野 -DEFAULT,3100534,330,341,1,三本松 -DEFAULT,3100535,330,341,0,赤目口 -DEFAULT,3100536,330,341,0,名張 -DEFAULT,3100537,330,341,0,桔梗が丘 -DEFAULT,3100538,330,341,0,美旗 -DEFAULT,3100539,330,341,0,伊賀神戸 -DEFAULT,3100540,330,341,0,青山町 -DEFAULT,3100541,330,341,1,伊賀上津 -DEFAULT,3100542,330,341,1,西青山 -DEFAULT,3100543,330,341,1,東青山 -DEFAULT,3100544,330,341,0,榊原温泉口 -DEFAULT,3100545,330,341,1,大三 -DEFAULT,3100546,330,341,1,伊勢石橋 -DEFAULT,3100547,330,341,1,川合高岡 -DEFAULT,3100548,330,341,0,伊勢中川 -DEFAULT,3100901,330,341,0,伊勢中川 -DEFAULT,3100902,330,341,1,伊勢中原 -DEFAULT,3100903,330,341,1,松ヶ崎 -DEFAULT,3100904,330,341,0,松阪 -DEFAULT,3100905,330,341,1,東松阪 -DEFAULT,3100906,330,341,1,櫛田 -DEFAULT,3100907,330,341,1,漕代 -DEFAULT,3100908,330,341,1,斎宮 -DEFAULT,3100909,330,341,1,明星 -DEFAULT,3100910,330,341,1,明野 -DEFAULT,3100911,330,341,1,小俣 -DEFAULT,3100912,330,341,1,宮町 -DEFAULT,3100913,330,341,0,伊勢市 -DEFAULT,3100914,330,341,0,宇治山田 -DEFAULT,3101001,330,341,0,宇治山田 -DEFAULT,3101002,330,341,0,五十鈴川 -DEFAULT,3101003,330,341,0,朝熊 -DEFAULT,3101004,330,341,0,池の浦 -DEFAULT,3101005,330,341,0,鳥羽 +DEFAULT,3100501,375,340,0,大阪上本町 +DEFAULT,3100502,375,340,0,鶴橋 +DEFAULT,3100503,375,340,1,今里 +DEFAULT,3100504,375,340,0,布施 +DEFAULT,3100505,375,340,1,俊徳道 +DEFAULT,3100506,375,340,1,長瀬 +DEFAULT,3100507,375,340,1,弥刀 +DEFAULT,3100508,375,340,1,久宝寺口 +DEFAULT,3100509,375,340,1,近鉄八尾 +DEFAULT,3100510,375,340,1,河内山本 +DEFAULT,3100511,375,340,1,高安 +DEFAULT,3100512,375,340,1,恩智 +DEFAULT,3100513,375,340,1,法善寺 +DEFAULT,3100514,375,340,1,堅下 +DEFAULT,3100515,375,340,1,安堂 +DEFAULT,3100516,375,340,0,河内国分 +DEFAULT,3100517,375,340,1,大阪教育大前 +DEFAULT,3100518,375,340,1,関屋 +DEFAULT,3100519,375,340,1,二上 +DEFAULT,3100520,375,340,1,近鉄下田 +DEFAULT,3100521,375,340,0,五位堂 +DEFAULT,3100522,375,340,1,築山 +DEFAULT,3100523,375,340,0,大和高田 +DEFAULT,3100524,375,340,1,松塚 +DEFAULT,3100525,375,340,1,真菅 +DEFAULT,3100526,375,340,0,大和八木 +DEFAULT,3100527,375,340,1,耳成 +DEFAULT,3100528,375,340,1,大福 +DEFAULT,3100529,375,340,0,桜井 +DEFAULT,3100530,375,340,0,大和朝倉 +DEFAULT,3100531,375,340,0,長谷寺 +DEFAULT,3100532,375,340,0,榛原 +DEFAULT,3100533,375,340,0,室生口大野 +DEFAULT,3100534,375,340,0,三本松 +DEFAULT,3100535,375,340,0,赤目口 +DEFAULT,3100536,375,340,0,名張 +DEFAULT,3100537,375,340,0,桔梗が丘 +DEFAULT,3100538,375,340,0,美旗 +DEFAULT,3100539,375,340,0,伊賀神戸 +DEFAULT,3100540,375,340,0,青山町 +DEFAULT,3100541,375,340,0,伊賀上津 +DEFAULT,3100542,375,340,0,西青山 +DEFAULT,3100543,375,340,0,東青山 +DEFAULT,3100544,375,340,0,榊原温泉口 +DEFAULT,3100545,375,340,1,大三 +DEFAULT,3100546,375,340,1,伊勢石橋 +DEFAULT,3100547,375,340,1,川合高岡 +DEFAULT,3100548,375,340,0,伊勢中川 +DEFAULT,3100901,375,340,0,伊勢中川 +DEFAULT,3100902,375,340,1,伊勢中原 +DEFAULT,3100903,375,340,1,松ヶ崎 +DEFAULT,3100904,375,340,0,松阪 +DEFAULT,3100905,375,340,1,東松阪 +DEFAULT,3100906,375,340,1,櫛田 +DEFAULT,3100907,375,340,1,漕代 +DEFAULT,3100908,375,340,1,斎宮 +DEFAULT,3100909,375,340,1,明星 +DEFAULT,3100910,375,340,1,明野 +DEFAULT,3100911,375,340,1,小俣 +DEFAULT,3100912,375,340,1,宮町 +DEFAULT,3100913,375,340,0,伊勢市 +DEFAULT,3100914,375,340,0,宇治山田 +DEFAULT,3101001,375,340,0,宇治山田 +DEFAULT,3101002,375,340,0,五十鈴川 +DEFAULT,3100501,376,341,0,大阪上本町 +DEFAULT,3100502,376,341,0,鶴橋 +DEFAULT,3100503,376,341,1,今里 +DEFAULT,3100504,376,341,1,布施 +DEFAULT,3100505,376,341,1,俊徳道 +DEFAULT,3100506,376,341,1,長瀬 +DEFAULT,3100507,376,341,1,弥刀 +DEFAULT,3100508,376,341,1,久宝寺口 +DEFAULT,3100509,376,341,1,近鉄八尾 +DEFAULT,3100510,376,341,1,河内山本 +DEFAULT,3100511,376,341,1,高安 +DEFAULT,3100512,376,341,1,恩智 +DEFAULT,3100513,376,341,1,法善寺 +DEFAULT,3100514,376,341,1,堅下 +DEFAULT,3100515,376,341,1,安堂 +DEFAULT,3100516,376,341,1,河内国分 +DEFAULT,3100517,376,341,1,大阪教育大前 +DEFAULT,3100518,376,341,1,関屋 +DEFAULT,3100519,376,341,1,二上 +DEFAULT,3100520,376,341,1,近鉄下田 +DEFAULT,3100521,376,341,0,五位堂 +DEFAULT,3100522,376,341,1,築山 +DEFAULT,3100523,376,341,0,大和高田 +DEFAULT,3100524,376,341,1,松塚 +DEFAULT,3100525,376,341,1,真菅 +DEFAULT,3100526,376,341,0,大和八木 +DEFAULT,3100527,376,341,1,耳成 +DEFAULT,3100528,376,341,1,大福 +DEFAULT,3100529,376,341,0,桜井 +DEFAULT,3100530,376,341,1,大和朝倉 +DEFAULT,3100531,376,341,1,長谷寺 +DEFAULT,3100532,376,341,0,榛原 +DEFAULT,3100533,376,341,0,室生口大野 +DEFAULT,3100534,376,341,1,三本松 +DEFAULT,3100535,376,341,0,赤目口 +DEFAULT,3100536,376,341,0,名張 +DEFAULT,3100537,376,341,0,桔梗が丘 +DEFAULT,3100538,376,341,0,美旗 +DEFAULT,3100539,376,341,0,伊賀神戸 +DEFAULT,3100540,376,341,0,青山町 +DEFAULT,3100541,376,341,1,伊賀上津 +DEFAULT,3100542,376,341,1,西青山 +DEFAULT,3100543,376,341,1,東青山 +DEFAULT,3100544,376,341,0,榊原温泉口 +DEFAULT,3100545,376,341,1,大三 +DEFAULT,3100546,376,341,1,伊勢石橋 +DEFAULT,3100547,376,341,1,川合高岡 +DEFAULT,3100548,376,341,0,伊勢中川 +DEFAULT,3100901,376,341,0,伊勢中川 +DEFAULT,3100902,376,341,1,伊勢中原 +DEFAULT,3100903,376,341,1,松ヶ崎 +DEFAULT,3100904,376,341,0,松阪 +DEFAULT,3100905,376,341,1,東松阪 +DEFAULT,3100906,376,341,1,櫛田 +DEFAULT,3100907,376,341,1,漕代 +DEFAULT,3100908,376,341,1,斎宮 +DEFAULT,3100909,376,341,1,明星 +DEFAULT,3100910,376,341,1,明野 +DEFAULT,3100911,376,341,1,小俣 +DEFAULT,3100912,376,341,1,宮町 +DEFAULT,3100913,376,341,0,伊勢市 +DEFAULT,3100914,376,341,0,宇治山田 +DEFAULT,3101001,376,341,0,宇治山田 +DEFAULT,3101002,376,341,0,五十鈴川 +DEFAULT,3101003,376,341,0,朝熊 +DEFAULT,3101004,376,341,0,池の浦 +DEFAULT,3101005,376,341,0,鳥羽 DEFAULT,3100901,100,342,0,伊勢中川 DEFAULT,3100902,100,342,0,伊勢中原 DEFAULT,3100903,100,342,0,松ヶ崎 @@ -13037,44 +13037,6 @@ DEFAULT,3101513,100,342,0,志摩横山 DEFAULT,3101514,100,342,0,鵜方 DEFAULT,3101515,100,342,0,志摩神明 DEFAULT,3101516,100,342,0,賢島 -DEFAULT,3100901,304,343,0,伊勢中川 -DEFAULT,3100902,304,343,1,伊勢中原 -DEFAULT,3100903,304,343,1,松ヶ崎 -DEFAULT,3100904,304,343,0,松阪 -DEFAULT,3100905,304,343,1,東松阪 -DEFAULT,3100906,304,343,1,櫛田 -DEFAULT,3100907,304,343,1,漕代 -DEFAULT,3100908,304,343,1,斎宮 -DEFAULT,3100909,304,343,1,明星 -DEFAULT,3100910,304,343,1,明野 -DEFAULT,3100911,304,343,1,小俣 -DEFAULT,3100912,304,343,2,宮町 -DEFAULT,3100913,304,343,0,伊勢市 -DEFAULT,3100914,304,343,0,宇治山田 -DEFAULT,3101001,304,343,0,宇治山田 -DEFAULT,3101002,304,343,0,五十鈴川 -DEFAULT,3101003,304,343,0,朝熊 -DEFAULT,3101004,304,343,0,池の浦 -DEFAULT,3101005,304,343,0,鳥羽 -DEFAULT,3100901,330,344,0,伊勢中川 -DEFAULT,3100902,330,344,1,伊勢中原 -DEFAULT,3100903,330,344,1,松ヶ崎 -DEFAULT,3100904,330,344,0,松阪 -DEFAULT,3100905,330,344,1,東松阪 -DEFAULT,3100906,330,344,1,櫛田 -DEFAULT,3100907,330,344,1,漕代 -DEFAULT,3100908,330,344,1,斎宮 -DEFAULT,3100909,330,344,1,明星 -DEFAULT,3100910,330,344,1,明野 -DEFAULT,3100911,330,344,1,小俣 -DEFAULT,3100912,330,344,1,宮町 -DEFAULT,3100913,330,344,0,伊勢市 -DEFAULT,3100914,330,344,0,宇治山田 -DEFAULT,3101001,330,344,0,宇治山田 -DEFAULT,3101002,330,344,0,五十鈴川 -DEFAULT,3101003,330,344,0,朝熊 -DEFAULT,3101004,330,344,0,池の浦 -DEFAULT,3101005,330,344,0,鳥羽 DEFAULT,3100101,306,345,0,大阪難波 DEFAULT,3100102,306,345,1,近鉄日本橋 DEFAULT,3100103,306,345,0,大阪上本町 @@ -14274,45 +14236,45 @@ DEFAULT,3102513,100,373,0,久津川 DEFAULT,3102514,100,373,0,寺田 DEFAULT,3102515,100,373,0,富野荘 DEFAULT,3102516,100,373,0,新田辺 -DEFAULT,9961001,304,374,0,国際会館 -DEFAULT,9961002,304,374,0,松ヶ崎 -DEFAULT,9961003,304,374,0,北山 -DEFAULT,9961004,304,374,0,北大路 -DEFAULT,9961005,304,374,0,鞍馬口 -DEFAULT,9961006,304,374,0,今出川 -DEFAULT,9961007,304,374,0,丸太町 -DEFAULT,9961008,304,374,0,烏丸御池 -DEFAULT,9961009,304,374,0,四条 -DEFAULT,9961010,304,374,0,五条 -DEFAULT,9961011,304,374,0,京都 -DEFAULT,9961012,304,374,0,九条 -DEFAULT,9961013,304,374,0,十条 -DEFAULT,9961014,304,374,0,くいな橋 -DEFAULT,9961015,304,374,0,竹田 -DEFAULT,3102505,304,374,0,竹田 -DEFAULT,3102506,304,374,1,伏見 -DEFAULT,3102507,304,374,0,近鉄丹波橋 -DEFAULT,3102508,304,374,0,桃山御陵前 -DEFAULT,3102509,304,374,1,向島 -DEFAULT,3102510,304,374,1,小倉 -DEFAULT,3102511,304,374,1,伊勢田 -DEFAULT,3102512,304,374,0,大久保 -DEFAULT,3102513,304,374,1,久津川 -DEFAULT,3102514,304,374,1,寺田 -DEFAULT,3102515,304,374,1,富野荘 -DEFAULT,3102516,304,374,0,新田辺 -DEFAULT,3102517,304,374,1,興戸 -DEFAULT,3102518,304,374,1,三山木 -DEFAULT,3102519,304,374,1,近鉄宮津 -DEFAULT,3102520,304,374,1,狛田 -DEFAULT,3102521,304,374,0,新祝園 -DEFAULT,3102522,304,374,1,木津川台 -DEFAULT,3102523,304,374,1,山田川 -DEFAULT,3102524,304,374,0,高の原 -DEFAULT,3102525,304,374,1,平城 -DEFAULT,3102526,304,374,0,大和西大寺 -DEFAULT,3102527,304,374,0,新大宮 -DEFAULT,3102528,304,374,0,近鉄奈良 +DEFAULT,9961001,375,374,0,国際会館 +DEFAULT,9961002,375,374,0,松ヶ崎 +DEFAULT,9961003,375,374,0,北山 +DEFAULT,9961004,375,374,0,北大路 +DEFAULT,9961005,375,374,0,鞍馬口 +DEFAULT,9961006,375,374,0,今出川 +DEFAULT,9961007,375,374,0,丸太町 +DEFAULT,9961008,375,374,0,烏丸御池 +DEFAULT,9961009,375,374,0,四条 +DEFAULT,9961010,375,374,0,五条 +DEFAULT,9961011,375,374,0,京都 +DEFAULT,9961012,375,374,0,九条 +DEFAULT,9961013,375,374,0,十条 +DEFAULT,9961014,375,374,0,くいな橋 +DEFAULT,9961015,375,374,0,竹田 +DEFAULT,3102505,375,374,0,竹田 +DEFAULT,3102506,375,374,1,伏見 +DEFAULT,3102507,375,374,0,近鉄丹波橋 +DEFAULT,3102508,375,374,0,桃山御陵前 +DEFAULT,3102509,375,374,1,向島 +DEFAULT,3102510,375,374,1,小倉 +DEFAULT,3102511,375,374,1,伊勢田 +DEFAULT,3102512,375,374,0,大久保 +DEFAULT,3102513,375,374,1,久津川 +DEFAULT,3102514,375,374,1,寺田 +DEFAULT,3102515,375,374,1,富野荘 +DEFAULT,3102516,375,374,0,新田辺 +DEFAULT,3102517,375,374,1,興戸 +DEFAULT,3102518,375,374,1,三山木 +DEFAULT,3102519,375,374,1,近鉄宮津 +DEFAULT,3102520,375,374,1,狛田 +DEFAULT,3102521,375,374,0,新祝園 +DEFAULT,3102522,375,374,1,木津川台 +DEFAULT,3102523,375,374,1,山田川 +DEFAULT,3102524,375,374,0,高の原 +DEFAULT,3102525,375,374,1,平城 +DEFAULT,3102526,375,374,0,大和西大寺 +DEFAULT,3102527,375,374,0,新大宮 +DEFAULT,3102528,375,374,0,近鉄奈良 DEFAULT,1130201,100,375,0,大崎 DEFAULT,1130202,100,375,0,五反田 DEFAULT,1130203,100,375,0,目黒 @@ -14374,25 +14336,25 @@ DEFAULT,1132702,230,376,1,酒々井 DEFAULT,1132703,230,376,2,成田 DEFAULT,1132704,230,376,0,空港第2ビル(第2旅客ターミナル) DEFAULT,1132705,230,376,0,成田空港(第1旅客ターミナル) -DEFAULT,3102501,304,377,0,京都 -DEFAULT,3102502,304,377,0,東寺 -DEFAULT,3102503,304,377,1,十条 -DEFAULT,3102504,304,377,1,上鳥羽口 -DEFAULT,3102505,304,377,0,竹田 -DEFAULT,3102506,304,377,1,伏見 -DEFAULT,3102507,304,377,0,近鉄丹波橋 -DEFAULT,3102508,304,377,0,桃山御陵前 -DEFAULT,3102509,304,377,1,向島 -DEFAULT,3102510,304,377,1,小倉 -DEFAULT,3102511,304,377,1,伊勢田 -DEFAULT,3102512,304,377,0,大久保 -DEFAULT,3102513,304,377,1,久津川 -DEFAULT,3102514,304,377,1,寺田 -DEFAULT,3102515,304,377,1,富野荘 -DEFAULT,3102516,304,377,0,新田辺 -DEFAULT,3102517,304,377,0,興戸 -DEFAULT,3102518,304,377,0,三山木 -DEFAULT,3102519,304,377,0,近鉄宮津 +DEFAULT,3102501,375,377,0,京都 +DEFAULT,3102502,375,377,0,東寺 +DEFAULT,3102503,375,377,1,十条 +DEFAULT,3102504,375,377,1,上鳥羽口 +DEFAULT,3102505,375,377,0,竹田 +DEFAULT,3102506,375,377,1,伏見 +DEFAULT,3102507,375,377,0,近鉄丹波橋 +DEFAULT,3102508,375,377,0,桃山御陵前 +DEFAULT,3102509,375,377,1,向島 +DEFAULT,3102510,375,377,1,小倉 +DEFAULT,3102511,375,377,1,伊勢田 +DEFAULT,3102512,375,377,0,大久保 +DEFAULT,3102513,375,377,1,久津川 +DEFAULT,3102514,375,377,1,寺田 +DEFAULT,3102515,375,377,1,富野荘 +DEFAULT,3102516,375,377,0,新田辺 +DEFAULT,3102517,375,377,0,興戸 +DEFAULT,3102518,375,377,0,三山木 +DEFAULT,3102519,375,377,0,近鉄宮津 DEFAULT,1190202,231,378,0,門司港 DEFAULT,1190203,231,378,1,小森江 DEFAULT,1190204,231,378,0,門司 @@ -22099,20 +22061,20 @@ DEFAULT,3101902,100,616,0,柳 DEFAULT,3101903,100,616,0,鈴鹿市 DEFAULT,3101904,100,616,0,三日市 DEFAULT,3101905,100,616,0,平田町 -DEFAULT,3102721,304,617,0,近鉄四日市 -DEFAULT,3102722,304,617,1,新正 -DEFAULT,3102723,304,617,1,海山道 -DEFAULT,3102724,304,617,0,塩浜 -DEFAULT,3102725,304,617,1,北楠 -DEFAULT,3102726,304,617,1,楠 -DEFAULT,3102727,304,617,1,長太ノ浦 -DEFAULT,3102728,304,617,1,箕田 -DEFAULT,3102729,304,617,0,伊勢若松 -DEFAULT,3101901,304,617,0,伊勢若松 -DEFAULT,3101902,304,617,0,柳 -DEFAULT,3101903,304,617,0,鈴鹿市 -DEFAULT,3101904,304,617,0,三日市 -DEFAULT,3101905,304,617,0,平田町 +DEFAULT,3102721,375,617,0,近鉄四日市 +DEFAULT,3102722,375,617,1,新正 +DEFAULT,3102723,375,617,1,海山道 +DEFAULT,3102724,375,617,0,塩浜 +DEFAULT,3102725,375,617,1,北楠 +DEFAULT,3102726,375,617,1,楠 +DEFAULT,3102727,375,617,1,長太ノ浦 +DEFAULT,3102728,375,617,1,箕田 +DEFAULT,3102729,375,617,0,伊勢若松 +DEFAULT,3101901,375,617,0,伊勢若松 +DEFAULT,3101902,375,617,0,柳 +DEFAULT,3101903,375,617,0,鈴鹿市 +DEFAULT,3101904,375,617,0,三日市 +DEFAULT,3101905,375,617,0,平田町 DEFAULT,2100130,100,618,0,森林公園 DEFAULT,2100129,100,618,0,東松山 DEFAULT,2100128,100,618,0,高坂 From 00deaf577d75b7f0ccd4d1fadac55700b2823f27 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E3=81=BF=E3=81=A3=E3=81=9F=E3=82=93?= <147319703+mittan12@users.noreply.github.com> Date: Fri, 6 Mar 2026 10:08:09 +0900 Subject: [PATCH 6/6] =?UTF-8?q?=E8=BF=91=E9=89=84=E7=A8=AE=E5=88=A5?= =?UTF-8?q?=E4=BF=AE=E6=AD=A3?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 駅コード修正 --- data/5!station_station_types.csv | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/data/5!station_station_types.csv b/data/5!station_station_types.csv index 7b416693..d1772cdd 100644 --- a/data/5!station_station_types.csv +++ b/data/5!station_station_types.csv @@ -12399,7 +12399,7 @@ DEFAULT,3100315,374,326,1,道明寺 DEFAULT,3100316,374,326,0,古市 DEFAULT,3100317,374,326,1,駒ヶ谷 DEFAULT,3100318,374,326,1,上ノ太子 -DEFAULT,3100374,374,326,1,二上山 +DEFAULT,3100319,374,326,1,二上山 DEFAULT,3100320,374,326,1,二上神社口 DEFAULT,3100321,374,326,1,当麻寺 DEFAULT,3100322,374,326,1,磐城 @@ -41012,4 +41012,4 @@ DEFAULT,9942017,102,1220,0,あわら湯のまち DEFAULT,9942018,102,1220,1,水居 DEFAULT,9942019,102,1220,0,三国神社 DEFAULT,9942020,102,1220,0,三国 -DEFAULT,9942021,102,1220,0,三国港 \ No newline at end of file +DEFAULT,9942021,102,1220,0,三国港