History of cptond_china_transit
The changes to the source code of this entry in the repository, newest first, each with the differences it made to the entry's files. They are also listed on GitLab.
[entries] Add the date when each entry was added, and remove icon_hash beff7c9
The dates are those of the commits that first added the entries' files (2020-08-11 for the entries of the initial commit); renamed entries keep the date of their first name. Changed: none
diff --git a/entries/cptond_china_transit/__init__.py b/entries/cptond_china_transit/__init__.py
index 3b9fbfc..9794015 100644
--- a/entries/cptond_china_transit/__init__.py
+++ b/entries/cptond_china_transit/__init__.py
@@ -41,7 +41,7 @@ citation = [('L. Wang, H. Wei, Y. Guan, L. Ouyang, D. Xu, X. Han, M. Zhang, M. C
('L. Wang, H. Wei, Y. Guan, L. Ouyang, D. Xu, X. Han, M. Zhang, M. Chen, D. Sun, D. Gong, Z. Zhang, X. Zhang, X. Zhang, "CPTOND-2025." figshare, dataset, version 2 (2025)', 'https://doi.org/10.6084/m9.figshare.29377427.v2')]
upstream_license = ("Creative Commons Attribution 4.0 International (CC BY 4.0)",
"https://creativecommons.org/licenses/by/4.0/")
-icon_hash = None
+date_added = '2026-10-27'
# The archive CPTOND-2025.zip (version 2 of the dataset), by figshare file id
upstream_prefix = 'https://api.figshare.com/v2/file/download'
archive = "58153174:zip:CPTOND-2025/dataset"
[entries] Prefix the properties in the descriptions with (Node), (Edge) or (Graph) bc3dbbd
The lists of properties at the end of the descriptions marked edge properties with "Edge" but left node properties unmarked, or grouped them in "Nodes: `a`, ...; `b`, ..." items, so the kind of a property was often unclear. Every item now starts with (Node), (Edge) or (Graph), followed by the property names, a colon and the explanation, one item per property or group of properties; qualifications (kinds of nodes, networks) follow the names in parentheses. Properties that were only named got a short explanation, and a few items that named the wrong kind of property were corrected.
diff --git a/entries/cptond_china_transit/__init__.py b/entries/cptond_china_transit/__init__.py
index fa3014b..3b9fbfc 100644
--- a/entries/cptond_china_transit/__init__.py
+++ b/entries/cptond_china_transit/__init__.py
@@ -27,9 +27,14 @@ There is one network for each city and mode, named after the city, in pinyin wit
The networks have the following properties:
-- Nodes: `name`, the name of the stop (Chinese); `name_en`, its (automatic) English translation; `stop_id`, its id in the data; `num_routes`, the number of routes serving it (counting the two directions of a route separately); `latitude` and `longitude`, its position.
-- Edges: `num_routes`, the number of routes passing through the segment; `distance`, its length along the route (km).
-- Network: `city_cn` and `city_en`, the name of the city in Chinese and (automatically translated) in English; `mode`, `bus` or `metro`."""
+- (Node) `name`: the name of the stop (Chinese).
+- (Node) `name_en`: its (automatic) English translation.
+- (Node) `stop_id`: its id in the data.
+- (Node) `num_routes`: the number of routes serving it (counting the two directions of a route separately).
+- (Node) `latitude` and `longitude`: its position.
+- (Edge) `num_routes`: the number of routes passing through the segment.
+- (Edge) `distance`: its length along the route (km).
+- (Graph) `city_cn` and `city_en`, the name of the city in Chinese and (automatically translated) in English; `mode`, `bus` or `metro`."""
tags = ['Transportation', 'Public Transport', 'Spatial', 'Weighted', 'Metadata']
url = ['https://doi.org/10.6084/m9.figshare.29377427', 'https://8684.cn', 'https://lbs.amap.com']
citation = [('L. Wang, H. Wei, Y. Guan, L. Ouyang, D. Xu, X. Han, M. Zhang, M. Chen, D. Sun, D. Gong, Z. Zhang, X. Zhang, X. Zhang, "China Public Transport Operation Network Dataset (CPTOND-2025): National-Scale Bus-Metro Vector Dataset." Scientific Data 13, 188 (2026)', 'https://doi.org/10.1038/s41597-025-06505-4'),
[entries] Remove unneeded code from the entries 2c69f19
Unneeded "global files" statements, unused imports and variables, f-strings without placeholders, duplicate keys in foodweb_little_rock (keeping the order and the values used), and the second definition of get_lines() in twitter_higgs. Nothing changes in the networks or descriptions. Changed: none
diff --git a/entries/cptond_china_transit/__init__.py b/entries/cptond_china_transit/__init__.py
index f407b18..fa3014b 100644
--- a/entries/cptond_china_transit/__init__.py
+++ b/entries/cptond_china_transit/__init__.py
@@ -487,7 +487,6 @@ def read_shp_points(data):
@coerce_props()
@annotate()
def parse(alts=None):
- global files
name = __name__.split(".")[-1]
for (fnames, alt, fmt), (mode, d, city, py) in zip(files, cities):
if alts is not None and alt not in alts:
[entries] New entry: cptond_china_transit 4c4be52
diff --git a/entries/cptond_china_transit/__init__.py b/entries/cptond_china_transit/__init__.py
new file mode 100644
index 0000000..f407b18
--- /dev/null
+++ b/entries/cptond_china_transit/__init__.py
@@ -0,0 +1,533 @@
+#! /usr/bin/env python
+# -*- coding: utf-8 -*-
+#
+# Copyright (C) 2026 Tiago de Paula Peixoto <tiago@skewed.de>
+#
+# This program is free software: you can redistribute it and/or modify it under
+# the terms of the GNU Affero General Public License as published by the Free
+# Software Foundation, either version 3 of the License, or (at your option) any
+# later version.
+#
+# This program is distributed in the hope that it will be useful, but WITHOUT
+# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
+# FOR A PARTICULAR PURPOSE. See the GNU Affero General Public License for more
+# details.
+#
+# You should have received a copy of the GNU Affero General Public License along
+# with this program. If not, see <http://www.gnu.org/licenses/>.
+
+import struct
+
+from .. import *
+
+title = "Bus and metro networks of Chinese cities (CPTOND-2025)"
+description = """Bus networks of 350 cities and metro networks of 46 cities in mainland China, Hong Kong and Macao (and one metro network of Taiwan, `taiwan_metro`, around Taipei), from the China Public Transport Operation Network Dataset (CPTOND-2025) compiled by L. Wang, H. Wei, Y. Guan and collaborators. The routes and stops were collected in June 2025 by systematically enumerating the routes listed by the public transport information platform 8684 (8684.cn) and retrieving their courses, stops and operational attributes from the Amap (Gaode) map API; the coordinates were converted from the Chinese GCJ-02 system to WGS 84. The English names of stops and cities in the data are automatic translations of the Chinese names, and some are mistranslations (e.g. the city of Jixi, 鸡西, appears as "Chicken West", and Dezhou, 德州, as "Texas").
+
+There is one network for each city and mode, named after the city, in pinyin without tones, and the mode (e.g. `beijing_bus`, `beijing_metro`, `jixi_bus`, `xianggang_metro`), since the English names of the data are automatic translations, some of them wrong (e.g. "Chicken West" for 鸡西, Jixi); the five pairs of cities with the same name in pinyin are told apart by their province (e.g. `fuzhou_fujian_bus` for 福州 and `fuzhou_jiangxi_bus` for 抚州); the network properties `city_cn` and `city_en` give the city's name. A node is a stop (bus stop or metro station, deduplicated over the routes of the city), and a directed edge from stop $i$ to stop $j$ means that some route serves $j$ immediately after $i$; the edges are the segments between consecutive stops provided by the authors, aggregated over the routes and directions, with the number of routes passing through them. Some segments join a stop to itself (9,853 self-loops over all networks), presumably where consecutive stops of a route share the same stop id. The courses of the segments and routes (as polylines) are not included, and neither are the routes themselves with their operational attributes (operating hours, fares, companies). The dataset has two versions on figshare (July and September 2025); the second, used here, separates the bus networks of five pairs of cities with the same name in pinyin (Fuzhou, Suzhou, Taizhou, Yichun and Yulin), which are merged in the first.
+
+The networks have the following properties:
+
+- Nodes: `name`, the name of the stop (Chinese); `name_en`, its (automatic) English translation; `stop_id`, its id in the data; `num_routes`, the number of routes serving it (counting the two directions of a route separately); `latitude` and `longitude`, its position.
+- Edges: `num_routes`, the number of routes passing through the segment; `distance`, its length along the route (km).
+- Network: `city_cn` and `city_en`, the name of the city in Chinese and (automatically translated) in English; `mode`, `bus` or `metro`."""
+tags = ['Transportation', 'Public Transport', 'Spatial', 'Weighted', 'Metadata']
+url = ['https://doi.org/10.6084/m9.figshare.29377427', 'https://8684.cn', 'https://lbs.amap.com']
+citation = [('L. Wang, H. Wei, Y. Guan, L. Ouyang, D. Xu, X. Han, M. Zhang, M. Chen, D. Sun, D. Gong, Z. Zhang, X. Zhang, X. Zhang, "China Public Transport Operation Network Dataset (CPTOND-2025): National-Scale Bus-Metro Vector Dataset." Scientific Data 13, 188 (2026)', 'https://doi.org/10.1038/s41597-025-06505-4'),
+ ('L. Wang, H. Wei, Y. Guan, L. Ouyang, D. Xu, X. Han, M. Zhang, M. Chen, D. Sun, D. Gong, Z. Zhang, X. Zhang, X. Zhang, "CPTOND-2025." figshare, dataset, version 2 (2025)', 'https://doi.org/10.6084/m9.figshare.29377427.v2')]
+upstream_license = ("Creative Commons Attribution 4.0 International (CC BY 4.0)",
+ "https://creativecommons.org/licenses/by/4.0/")
+icon_hash = None
+# The archive CPTOND-2025.zip (version 2 of the dataset), by figshare file id
+upstream_prefix = 'https://api.figshare.com/v2/file/download'
+archive = "58153174:zip:CPTOND-2025/dataset"
+# (mode, directory, city, name) of each network: the directory and the city
+# as in the file names of the data, and the name of the city in pinyin
+cities = [
+ ('bus', 'Aksu', 'aksu', 'akesu'),
+ ('bus', 'Alar', 'alar', 'alaer'),
+ ('bus', 'Altai', 'altai', 'aleitai'),
+ ('bus', 'Anqing', 'anqing', 'anqing'),
+ ('bus', 'Anshan', 'anshan', 'anshan'),
+ ('bus', 'Anyang', 'anyang', 'anyang'),
+ ('bus', 'Baishali Autonomous County', 'baishali_autonomous_county', 'baishalizuzizhixian'),
+ ('bus', 'Baoding', 'baoding', 'baoding'),
+ ('bus', 'Baoji', 'baoji', 'baoji'),
+ ('bus', 'Baoshan', 'baoshan', 'baoshan'),
+ ('bus', 'Baoting Li and Miao Autonomous County', 'baoting_li_and_miao_autonomous_county', 'baotinglizumiaozuzizhixian'),
+ ('bus', 'Bayannur', 'bayannur', 'bayannaoer'),
+ ('bus', 'Bayingolin Mongolia', 'bayingolin_mongolia', 'bayinguolengmenggu'),
+ ('bus', 'Bazhong', 'bazhong', 'bazhong'),
+ ('bus', 'Beijing', 'beijing', 'beijing'),
+ ('bus', 'Bengbu', 'bengbu', 'bengbu'),
+ ('bus', 'Benxi', 'benxi', 'benxi'),
+ ('bus', 'Bijie', 'bijie', 'bijie'),
+ ('bus', 'Binzhou', 'binzhou', 'binzhou'),
+ ('bus', 'Black River', 'black_river', 'heihe'),
+ ('bus', 'Bortala Mongolia', 'bortala_mongolia', 'boertalamenggu'),
+ ('bus', 'Bose', 'bose', 'baise'),
+ ('bus', 'Bozhou', 'bozhou', 'bozhou'),
+ ('bus', 'Cangzhou', 'cangzhou', 'cangzhou'),
+ ('bus', 'Central defender', 'central_defender', 'zhongwei'),
+ ('bus', 'Changchun', 'changchun', 'changchun'),
+ ('bus', 'Changde', 'changde', 'changde'),
+ ('bus', 'Changji Hui', 'changji_hui', 'changjihuizu'),
+ ('bus', 'Changjiang Li Autonomous County', 'changjiang_li_autonomous_county', 'changjianglizuzizhixian'),
+ ('bus', 'Changsha', 'changsha', 'changsha'),
+ ('bus', 'Changzhi', 'changzhi', 'changzhi'),
+ ('bus', 'Changzhou', 'changzhou', 'changzhou'),
+ ('bus', 'Chao yang', 'chao_yang', 'chaoyang'),
+ ('bus', 'Chengde', 'chengde', 'chengde'),
+ ('bus', 'Chengdu', 'chengdu', 'chengdu'),
+ ('bus', 'Chengmai County', 'chengmai_county', 'chengmaixian'),
+ ('bus', 'Chenzhou', 'chenzhou', 'chenzhou'),
+ ('bus', 'Chicken West', 'chicken_west', 'jixi'),
+ ('bus', 'Chifeng', 'chifeng', 'chifeng'),
+ ('bus', 'Chizhou', 'chizhou', 'chizhou'),
+ ('bus', 'Chongqing', 'chongqing', 'chongqing'),
+ ('bus', 'Chongzuo', 'chongzuo', 'chongzuo'),
+ ('bus', 'Chuxiong Yi nationality', 'chuxiong_yi_nationality', 'chuxiongyizu'),
+ ('bus', 'Chuzhou', 'chuzhou', 'chuzhou'),
+ ('bus', 'Cool and cool', 'cool_and_cool', 'pingliang'),
+ ('bus', 'Dali Bai Nationality', 'dali_bai_nationality', 'dalibaizu'),
+ ('bus', 'Dalian', 'dalian', 'dalian'),
+ ('bus', 'Danton', 'danton', 'dandong'),
+ ('bus', 'Danzhou', 'danzhou', 'danzhou'),
+ ('bus', 'Daqing', 'daqing', 'daqing'),
+ ('bus', 'Datong', 'datong', 'datong'),
+ ('bus', 'Dazhou', 'dazhou', 'dazhou'),
+ ('bus', 'Dehong Dai and Jingpo people', 'dehong_dai_and_jingpo_people', 'dehongdaizujingpozu'),
+ ('bus', 'Deyang', 'deyang', 'deyang'),
+ ('bus', 'Dingxi', 'dingxi', 'dingxi'),
+ ('bus', 'Diqing Tibetans', 'diqing_tibetans', 'diqingzangzu'),
+ ('bus', 'Dongguan', 'dongguan', 'dongguan'),
+ ('bus', 'Dongying', 'dongying', 'dongying'),
+ ('bus', 'Eagle Pond', 'eagle_pond', 'yingtan'),
+ ('bus', 'Enshi Tujia Miao people', 'enshi_tujia_miao_people', 'enshitujiazumiaozu'),
+ ('bus', 'Ezhou', 'ezhou', 'ezhou'),
+ ('bus', 'Fangchenggang', 'fangchenggang', 'fangchenggang'),
+ ('bus', 'Five Finger Mountain', 'five_finger_mountain', 'wuzhishan'),
+ ('bus', 'Foshan', 'foshan', 'foshan'),
+ ('bus', 'Fushun', 'fushun', 'fushun'),
+ ('bus', 'Fuxin', 'fuxin', 'fuxin'),
+ ('bus', 'Fuyang', 'fuyang', 'fuyang'),
+ ('bus', 'Fuzhou', 'fuzhou', 'fuzhou_fujian'),
+ ('bus', 'Ganzhou', 'ganzhou', 'ganzhou'),
+ ('bus', 'Guangyuan', 'guangyuan', 'guangyuan'),
+ ('bus', 'Guangzhou', 'guangzhou', 'guangzhou'),
+ ('bus', 'Guigang', 'guigang', 'guigang'),
+ ('bus', 'Guilin', 'guilin', 'guilin'),
+ ('bus', 'Guiyang', 'guiyang', 'guiyang'),
+ ('bus', 'Guyuan', 'guyuan', 'guyuan'),
+ ('bus', 'Haidong', 'haidong', 'haidong'),
+ ('bus', 'Haikou', 'haikou', 'haikou'),
+ ('bus', 'Haixi Mongolian Tibetan', 'haixi_mongolian_tibetan', 'haiximengguzuzangzu'),
+ ('bus', 'Hami', 'hami', 'hami'),
+ ('bus', 'Handan', 'handan', 'handan'),
+ ('bus', 'Hangzhou', 'hangzhou', 'hangzhou'),
+ ('bus', 'Hanzhong', 'hanzhong', 'hanzhong'),
+ ('bus', 'Harbin', 'harbin', 'haerbin'),
+ ('bus', "Heaven's Gate", 'heaven_s_gate', 'tianmen'),
+ ('bus', 'Hebi', 'hebi', 'hebi'),
+ ('bus', 'Hefei', 'hefei', 'hefei'),
+ ('bus', 'Hegang', 'hegang', 'hegang'),
+ ('bus', 'Hengshui', 'hengshui', 'hengshui'),
+ ('bus', 'Hengyang', 'hengyang', 'hengyang'),
+ ('bus', 'Heze', 'heze', 'heze'),
+ ('bus', 'Hezhou', 'hezhou', 'hezhou'),
+ ('bus', 'Hohhot', 'hohhot', 'huhehaote'),
+ ('bus', 'Hong Kong', 'hong_kong', 'xianggang'),
+ ('bus', 'Honghe Hani and Yi people', 'honghe_hani_and_yi_people', 'honghehanizuyizu'),
+ ('bus', "Huai'an", 'huai_an', 'huaian'),
+ ('bus', 'Huaibei', 'huaibei', 'huaibei'),
+ ('bus', 'Huaihua', 'huaihua', 'huaihua'),
+ ('bus', 'Huainan', 'huainan', 'huainan'),
+ ('bus', 'Huanggang', 'huanggang', 'huanggang'),
+ ('bus', 'Huangshan', 'huangshan', 'huangshan'),
+ ('bus', 'Huizhou', 'huizhou', 'huizhou'),
+ ('bus', 'Huludao', 'huludao', 'huludao'),
+ ('bus', 'Hulunbuir', 'hulunbuir', 'hulunbeier'),
+ ('bus', 'Hung Yen', 'hung_yen', 'xingan'),
+ ('bus', 'Huzhou', 'huzhou', 'huzhou'),
+ ('bus', 'Ili Kazakh', 'ili_kazakh', 'yilihasake'),
+ ('bus', "Ji'an", 'ji_an', 'jian'),
+ ('bus', 'Jiamusi', 'jiamusi', 'jiamusi'),
+ ('bus', 'Jiangmen', 'jiangmen', 'jiangmen'),
+ ('bus', 'Jiaozuo', 'jiaozuo', 'jiaozuo'),
+ ('bus', 'Jiaxing', 'jiaxing', 'jiaxing'),
+ ('bus', 'Jiayuguan', 'jiayuguan', 'jiayuguan'),
+ ('bus', 'Jieyang', 'jieyang', 'jieyang'),
+ ('bus', 'Jilin', 'jilin', 'jilin'),
+ ('bus', 'Jinan', 'jinan', 'jinan'),
+ ('bus', 'Jinchang', 'jinchang', 'jinchang'),
+ ('bus', 'Jincheng', 'jincheng', 'jincheng'),
+ ('bus', 'Jingdezhen', 'jingdezhen', 'jingdezhen'),
+ ('bus', 'Jingmen', 'jingmen', 'jingmen'),
+ ('bus', 'Jingzhou', 'jingzhou', 'jingzhou'),
+ ('bus', 'Jinhua', 'jinhua', 'jinhua'),
+ ('bus', 'Jining', 'jining', 'jining'),
+ ('bus', 'Jinzhong', 'jinzhong', 'jinzhong'),
+ ('bus', 'Jinzhou', 'jinzhou', 'jinzhou'),
+ ('bus', 'Jiujiang', 'jiujiang', 'jiujiang'),
+ ('bus', 'Jiuquan', 'jiuquan', 'jiuquan'),
+ ('bus', 'Jiyuan', 'jiyuan', 'jiyuan'),
+ ('bus', 'Kaifeng', 'kaifeng', 'kaifeng'),
+ ('bus', 'Karamay', 'karamay', 'kelamayi'),
+ ('bus', 'Kashi', 'kashi', 'kashi'),
+ ('bus', 'Kizilsu Kirgiz', 'kizilsu_kirgiz', 'kezileisukeerkezi'),
+ ('bus', 'Kokdala', 'kokdala', 'kekedala'),
+ ('bus', 'Kunming', 'kunming', 'kunming'),
+ ('bus', 'Kunyu', 'kunyu', 'kunyu'),
+ ('bus', 'Langfang', 'langfang', 'langfang'),
+ ('bus', 'Lanzhou', 'lanzhou', 'lanzhou'),
+ ('bus', 'Ledong Li Autonomous County', 'ledong_li_autonomous_county', 'ledonglizuzizhixian'),
+ ('bus', 'Leshan', 'leshan', 'leshan'),
+ ('bus', 'Lhasa', 'lhasa', 'lasa'),
+ ('bus', 'Liangshan Yi people', 'liangshan_yi_people', 'liangshanyizu'),
+ ('bus', 'Lianyungang', 'lianyungang', 'lianyungang'),
+ ('bus', 'Liaocheng', 'liaocheng', 'liaocheng'),
+ ('bus', 'Liaoyang', 'liaoyang', 'liaoyang'),
+ ('bus', 'Liaoyuan', 'liaoyuan', 'liaoyuan'),
+ ('bus', 'Lijiang', 'lijiang', 'lijiang'),
+ ('bus', 'Lincang', 'lincang', 'lincang'),
+ ('bus', 'Linfen', 'linfen', 'linfen'),
+ ('bus', 'Lingao County', 'lingao_county', 'lingaoxian'),
+ ('bus', 'Lingshui Li Autonomous County', 'lingshui_li_autonomous_county', 'lingshuilizuzizhixian'),
+ ('bus', 'Linxia Hui', 'linxia_hui', 'linxiahuizu'),
+ ('bus', 'Linyi', 'linyi', 'linyi'),
+ ('bus', 'Liuzhou', 'liuzhou', 'liuzhou'),
+ ('bus', 'Longnan', 'longnan', 'longnan'),
+ ('bus', 'Longyan', 'longyan', 'longyan'),
+ ('bus', 'Loudi', 'loudi', 'loudi'),
+ ('bus', "Lu'an", 'lu_an', 'luan'),
+ ('bus', 'Luohe', 'luohe', 'luohe'),
+ ('bus', 'Luoyang', 'luoyang', 'luoyang'),
+ ('bus', 'Luzhou', 'luzhou', 'luzhou'),
+ ('bus', 'Lv Liang', 'lv_liang', 'lvliang'),
+ ('bus', 'Ma On Shan', 'ma_on_shan', 'maanshan'),
+ ('bus', 'Macau', 'macau', 'aomen'),
+ ('bus', 'Mannings', 'mannings', 'wanning'),
+ ('bus', 'Maoming', 'maoming', 'maoming'),
+ ('bus', 'Matsubara', 'matsubara', 'songyuan'),
+ ('bus', 'Meishan', 'meishan', 'meishan'),
+ ('bus', 'Meizhou', 'meizhou', 'meizhou'),
+ ('bus', 'Mianyang', 'mianyang', 'mianyang'),
+ ('bus', 'Miao and Dong ethnic groups in southeast Guizhou', 'miao_and_dong_ethnic_groups_in_southeast_guizhou', 'qiandongnanmiaozudongzu'),
+ ('bus', 'Mudanjiang', 'mudanjiang', 'mudanjiang'),
+ ('bus', 'Nanchang', 'nanchang', 'nanchang'),
+ ('bus', 'Nanchong', 'nanchong', 'nanchong'),
+ ('bus', 'Nanning', 'nanning', 'nanning'),
+ ('bus', 'Nanping', 'nanping', 'nanping'),
+ ('bus', 'Nantong', 'nantong', 'nantong'),
+ ('bus', 'Nanyang', 'nanyang', 'nanyang'),
+ ('bus', 'Neijiang', 'neijiang', 'neijiang'),
+ ('bus', 'Ningde', 'ningde', 'ningde'),
+ ('bus', 'North Sea', 'north_sea', 'beihai'),
+ ('bus', 'Nujiang Lisu people', 'nujiang_lisu_people', 'nujianglisuzu'),
+ ('bus', 'Nyingchi', 'nyingchi', 'linzhi'),
+ ('bus', 'Ordos', 'ordos', 'eerduosi'),
+ ('bus', 'Panjin', 'panjin', 'panjin'),
+ ('bus', 'Panzhihua', 'panzhihua', 'panzhihua'),
+ ('bus', 'Peach', 'peach', 'xiantao'),
+ ('bus', 'Pingxiang', 'pingxiang', 'pingxiang'),
+ ('bus', 'Putian', 'putian', 'putian'),
+ ('bus', 'Puyang', 'puyang', 'puyang'),
+ ('bus', 'Qamdo', 'qamdo', 'changdu'),
+ ('bus', 'Qianjiang', 'qianjiang', 'qianjiang'),
+ ('bus', 'Qiannan Buyi and Miao people', 'qiannan_buyi_and_miao_people', 'qiannanbuyizumiaozu'),
+ ('bus', 'Qingdao', 'qingdao', 'qingdao'),
+ ('bus', 'Qingyang', 'qingyang', 'qingyang'),
+ ('bus', 'Qingyuan', 'qingyuan', 'qingyuan'),
+ ('bus', 'Qinhuangdao', 'qinhuangdao', 'qinhuangdao'),
+ ('bus', 'Qinzhou', 'qinzhou', 'qinzhou'),
+ ('bus', 'Qionghai', 'qionghai', 'qionghai'),
+ ('bus', 'Qiqihar', 'qiqihar', 'qiqihaer'),
+ ('bus', 'Qitai River', 'qitai_river', 'qitaihe'),
+ ('bus', 'Quang An', 'quang_an', 'guangan'),
+ ('bus', 'Quanzhou', 'quanzhou', 'quanzhou'),
+ ('bus', 'Qujing', 'qujing', 'qujing'),
+ ('bus', 'Quzhou', 'quzhou', 'quzhou'),
+ ('bus', 'River pools', 'river_pools', 'hechi'),
+ ('bus', 'Sanmenxia', 'sanmenxia', 'sanmenxia'),
+ ('bus', 'Sanming', 'sanming', 'sanming'),
+ ('bus', 'Sanya', 'sanya', 'sanya'),
+ ('bus', 'Shanghai', 'shanghai', 'shanghai'),
+ ('bus', 'Shangluo', 'shangluo', 'shangluo'),
+ ('bus', 'Shangqiu', 'shangqiu', 'shangqiu'),
+ ('bus', 'Shangrao', 'shangrao', 'shangrao'),
+ ('bus', 'Shannan', 'shannan', 'shannan'),
+ ('bus', 'Shantou', 'shantou', 'shantou'),
+ ('bus', 'Shanwei', 'shanwei', 'shanwei'),
+ ('bus', 'Shaoguan', 'shaoguan', 'shaoguan'),
+ ('bus', 'Shaoxing', 'shaoxing', 'shaoxing'),
+ ('bus', 'Shaoyang', 'shaoyang', 'shaoyang'),
+ ('bus', 'Shennongjia Forest Area', 'shennongjia_forest_area', 'shennongjialinqu'),
+ ('bus', 'Shenyang', 'shenyang', 'shenyang'),
+ ('bus', 'Shenzhen', 'shenzhen', 'shenzhen'),
+ ('bus', 'Shigatse', 'shigatse', 'rikaze'),
+ ('bus', 'Shihezi', 'shihezi', 'shihezi'),
+ ('bus', 'Shijiazhuang', 'shijiazhuang', 'shijiazhuang'),
+ ('bus', 'Shiyan', 'shiyan', 'shiyan'),
+ ('bus', 'Shizui Mountain', 'shizui_mountain', 'shizuishan'),
+ ('bus', 'Shuangyashan', 'shuangyashan', 'shuangyashan'),
+ ('bus', 'Shuozhou', 'shuozhou', 'shuozhou'),
+ ('bus', 'Siping', 'siping', 'siping'),
+ ('bus', 'Six pans of water', 'six_pans_of_water', 'liupanshui'),
+ ('bus', 'Southwest Guizhou Buyi and Miao people', 'southwest_guizhou_buyi_and_miao_people', 'qianxinanbuyizumiaozu'),
+ ('bus', 'Suihua', 'suihua', 'suihua'),
+ ('bus', 'Suining', 'suining', 'suining'),
+ ('bus', 'Suizhou', 'suizhou', 'suizhou'),
+ ('bus', 'Suqian', 'suqian', 'suqian'),
+ ('bus', 'Suzhou', 'suzhou', 'suzhou_jiangsu'),
+ ('bus', "Tai'an", 'tai_an', 'taian'),
+ ('bus', 'Taiyuan', 'taiyuan', 'taiyuan'),
+ ('bus', 'Taizhou', 'taizhou', 'taizhou_zhejiang'),
+ ('bus', 'Tangshan', 'tangshan', 'tangshan'),
+ ('bus', 'Teluk Intan (Teluk)', 'teluk_intan_teluk', 'anshun'),
+ ('bus', 'Teochew', 'teochew', 'chaozhou'),
+ ('bus', 'Texas', 'texas', 'dezhou'),
+ ('bus', 'That song', 'that_song', 'naqu'),
+ ('bus', 'Tianjin', 'tianjin', 'tianjin'),
+ ('bus', 'Tianshui', 'tianshui', 'tianshui'),
+ ('bus', 'Tieling', 'tieling', 'tieling'),
+ ('bus', 'Tongchuan', 'tongchuan', 'tongchuan'),
+ ('bus', 'Tonghua', 'tonghua', 'tonghua'),
+ ('bus', 'Tongliao', 'tongliao', 'tongliao'),
+ ('bus', 'Tongling', 'tongling', 'tongling'),
+ ('bus', 'Tongren', 'tongren', 'tongren'),
+ ('bus', 'Tower City', 'tower_city', 'tacheng'),
+ ('bus', 'Tumshuk', 'tumshuk', 'tumushuke'),
+ ('bus', 'Turpan', 'turpan', 'tulufan'),
+ ('bus', 'Twin Rivers', 'twin_rivers', 'shuanghe'),
+ ('bus', 'Ulanqab', 'ulanqab', 'wulanchabu'),
+ ('bus', 'Wada', 'wada', 'hetian'),
+ ('bus', 'Weifang', 'weifang', 'weifang'),
+ ('bus', 'Weihai', 'weihai', 'weihai'),
+ ('bus', 'Weinan', 'weinan', 'weinan'),
+ ('bus', 'Well-being', 'well_being', 'ankang'),
+ ('bus', 'Wenchang', 'wenchang', 'wenchang'),
+ ('bus', 'Wenshan Zhuang and Miao', 'wenshan_zhuang_and_miao', 'wenshanzhuangzumiaozu'),
+ ('bus', 'Wenzhou', 'wenzhou', 'wenzhou'),
+ ('bus', 'White City', 'white_city', 'baicheng'),
+ ('bus', 'White Mountain', 'white_mountain', 'baishan'),
+ ('bus', 'Wu Wei', 'wu_wei', 'wuwei'),
+ ('bus', 'Wu Zhong', 'wu_zhong', 'wuzhong'),
+ ('bus', 'Wuhai', 'wuhai', 'wuhai'),
+ ('bus', 'Wuhan', 'wuhan', 'wuhan'),
+ ('bus', 'Wuhu', 'wuhu', 'wuhu'),
+ ('bus', 'Wujiaqu', 'wujiaqu', 'wujiaqu'),
+ ('bus', 'Wuxi', 'wuxi', 'wuxi'),
+ ('bus', 'Wuzhou', 'wuzhou', 'wuzhou'),
+ ('bus', 'Xiamen', 'xiamen', 'xiamen'),
+ ('bus', 'Xiangtan', 'xiangtan', 'xiangtan'),
+ ('bus', 'Xiangxi Tujia and Miao people', 'xiangxi_tujia_and_miao_people', 'xiangxitujiazumiaozu'),
+ ('bus', 'Xiangyang', 'xiangyang', 'xiangyang'),
+ ('bus', 'Xianning', 'xianning', 'xianning'),
+ ('bus', 'Xiaogan', 'xiaogan', 'xiaogan'),
+ ('bus', 'Xilin Gol', 'xilin_gol', 'xilinguole'),
+ ('bus', 'Xingtai', 'xingtai', 'xingtai'),
+ ('bus', 'Xining', 'xining', 'xining'),
+ ('bus', 'Xinxiang', 'xinxiang', 'xinxiang'),
+ ('bus', 'Xinyang', 'xinyang', 'xinyang'),
+ ('bus', 'Xinyu', 'xinyu', 'xinyu'),
+ ('bus', 'Xinzhou', 'xinzhou', 'xinzhou'),
+ ('bus', 'Xishuangbanna Dai people', 'xishuangbanna_dai_people', 'xishuangbannadaizu'),
+ ('bus', 'Xuancheng', 'xuancheng', 'xuancheng'),
+ ('bus', 'Xuchang', 'xuchang', 'xuchang'),
+ ('bus', 'Xuzhou', 'xuzhou', 'xuzhou'),
+ ('bus', "Ya'an", 'ya_an', 'yaan'),
+ ('bus', "Yan'an", 'yan_an', 'yanan'),
+ ('bus', 'Yanbian Koreans', 'yanbian_koreans', 'yanbianchaoxianzu'),
+ ('bus', 'Yancheng', 'yancheng', 'yancheng'),
+ ('bus', 'Yang Spring', 'yang_spring', 'yangquan'),
+ ('bus', 'Yangjiang', 'yangjiang', 'yangjiang'),
+ ('bus', 'Yangzhou', 'yangzhou', 'yangzhou'),
+ ('bus', 'Yantai', 'yantai', 'yantai'),
+ ('bus', 'Yellowstone', 'yellowstone', 'huangshi'),
+ ('bus', 'Yibin', 'yibin', 'yibin'),
+ ('bus', 'Yichang', 'yichang', 'yichang'),
+ ('bus', 'Yichun', 'yichun', 'yichun_jiangxi'),
+ ('bus', 'Yinchuan', 'yinchuan', 'yinchuan'),
+ ('bus', 'Yingkou', 'yingkou', 'yingkou'),
+ ('bus', 'Yiyang', 'yiyang', 'yiyang'),
+ ('bus', 'Yongzhou', 'yongzhou', 'yongzhou'),
+ ('bus', 'Yueyang', 'yueyang', 'yueyang'),
+ ('bus', 'Yulin', 'yulin', 'yulin_guangxi'),
+ ('bus', 'Yuncheng', 'yuncheng', 'yuncheng'),
+ ('bus', 'Yunfu', 'yunfu', 'yunfu'),
+ ('bus', 'Yuxi', 'yuxi', 'yuxi'),
+ ('bus', 'Zaozhuang', 'zaozhuang', 'zaozhuang'),
+ ('bus', 'Zhangjiajie', 'zhangjiajie', 'zhangjiajie'),
+ ('bus', 'Zhangjiakou', 'zhangjiakou', 'zhangjiakou'),
+ ('bus', 'Zhangye', 'zhangye', 'zhangye'),
+ ('bus', 'Zhangzhou', 'zhangzhou', 'zhangzhou'),
+ ('bus', 'Zhanjiang', 'zhanjiang', 'zhanjiang'),
+ ('bus', 'Zhaoqing', 'zhaoqing', 'zhaoqing'),
+ ('bus', 'Zhaotong', 'zhaotong', 'zhaotong'),
+ ('bus', 'Zhengzhou', 'zhengzhou', 'zhengzhou'),
+ ('bus', 'Zhenjiang', 'zhenjiang', 'zhenjiang'),
+ ('bus', 'Zhongshan', 'zhongshan', 'zhongshan'),
+ ('bus', 'Zhoukou', 'zhoukou', 'zhoukou'),
+ ('bus', 'Zhoushan', 'zhoushan', 'zhoushan'),
+ ('bus', 'Zhuhai', 'zhuhai', 'zhuhai'),
+ ('bus', 'Zhumadian', 'zhumadian', 'zhumadian'),
+ ('bus', 'Zhuzhou', 'zhuzhou', 'zhuzhou'),
+ ('bus', 'Zibo', 'zibo', 'zibo'),
+ ('bus', 'Zigong', 'zigong', 'zigong'),
+ ('bus', 'Ziyang', 'ziyang', 'ziyang'),
+ ('bus', 'Zunyi', 'zunyi', 'zunyi'),
+ ('bus', 'aspen', 'aspen', 'baiyang'),
+ ('bus', 'fu_zhou', 'fu_zhou', 'fuzhou_jiangxi'),
+ ('bus', 'guest', 'guest', 'laibin'),
+ ('bus', 'mesa', 'mesa', 'pingdingshan'),
+ ('bus', 'nanking', 'nanking', 'nanjing'),
+ ('bus', 'ningbo', 'ningbo', 'ningbo'),
+ ('bus', 'nova', 'nova', 'xinxing'),
+ ('bus', 'orient', 'orient', 'dongfang'),
+ ('bus', 'riverhead', 'riverhead', 'heyuan'),
+ ('bus', 'sian', 'sian', 'xian'),
+ ('bus', 'silver', 'silver', 'baiyin'),
+ ('bus', 'su_zhou', 'su_zhou', 'suzhou_anhui'),
+ ('bus', 'sunshine', 'sunshine', 'rizhao'),
+ ('bus', 'tai_zhou', 'tai_zhou', 'taizhou_jiangsu'),
+ ('bus', 'turban', 'turban', 'baotou'),
+ ('bus', 'urumchi', 'urumchi', 'wulumuqi'),
+ ('bus', 'yi_chun', 'yi_chun', 'yichun_heilongjiang'),
+ ('bus', 'yu_lin', 'yu_lin', 'yulin_shaanxi'),
+ ('metro', 'Beijing', 'beijing', 'beijing'),
+ ('metro', 'Changchun', 'changchun', 'changchun'),
+ ('metro', 'Changsha', 'changsha', 'changsha'),
+ ('metro', 'Changzhou', 'changzhou', 'changzhou'),
+ ('metro', 'Chengdu', 'chengdu', 'chengdu'),
+ ('metro', 'Chongqing', 'chongqing', 'chongqing'),
+ ('metro', 'Chuzhou', 'chuzhou', 'chuzhou'),
+ ('metro', 'Dalian', 'dalian', 'dalian'),
+ ('metro', 'Dongguan', 'dongguan', 'dongguan'),
+ ('metro', 'Foshan', 'foshan', 'foshan'),
+ ('metro', 'Fuzhou', 'fuzhou', 'fuzhou'),
+ ('metro', 'Guangzhou', 'guangzhou', 'guangzhou'),
+ ('metro', 'Guiyang', 'guiyang', 'guiyang'),
+ ('metro', 'Hangzhou', 'hangzhou', 'hangzhou'),
+ ('metro', 'Harbin', 'harbin', 'haerbin'),
+ ('metro', 'Hefei', 'hefei', 'hefei'),
+ ('metro', 'Hohhot', 'hohhot', 'huhehaote'),
+ ('metro', 'Hong Kong', 'hong_kong', 'xianggang'),
+ ('metro', 'Jinan', 'jinan', 'jinan'),
+ ('metro', 'Kunming', 'kunming', 'kunming'),
+ ('metro', 'Lanzhou', 'lanzhou', 'lanzhou'),
+ ('metro', 'Luoyang', 'luoyang', 'luoyang'),
+ ('metro', 'Nanchang', 'nanchang', 'nanchang'),
+ ('metro', 'Nanning', 'nanning', 'nanning'),
+ ('metro', 'Nantong', 'nantong', 'nantong'),
+ ('metro', 'Qingdao', 'qingdao', 'qingdao'),
+ ('metro', 'Shanghai', 'shanghai', 'shanghai'),
+ ('metro', 'Shaoxing', 'shaoxing', 'shaoxing'),
+ ('metro', 'Shenyang', 'shenyang', 'shenyang'),
+ ('metro', 'Shenzhen', 'shenzhen', 'shenzhen'),
+ ('metro', 'Shijiazhuang', 'shijiazhuang', 'shijiazhuang'),
+ ('metro', 'Suzhou', 'suzhou', 'suzhou'),
+ ('metro', 'Taiwan', 'taiwan', 'taiwan'),
+ ('metro', 'Taiyuan', 'taiyuan', 'taiyuan'),
+ ('metro', 'Tianjin', 'tianjin', 'tianjin'),
+ ('metro', 'Wenzhou', 'wenzhou', 'wenzhou'),
+ ('metro', 'Wuhan', 'wuhan', 'wuhan'),
+ ('metro', 'Wuhu', 'wuhu', 'wuhu'),
+ ('metro', 'Wuxi', 'wuxi', 'wuxi'),
+ ('metro', 'Xiamen', 'xiamen', 'xiamen'),
+ ('metro', 'Xuzhou', 'xuzhou', 'xuzhou'),
+ ('metro', 'Zhengzhou', 'zhengzhou', 'zhengzhou'),
+ ('metro', 'nanking', 'nanking', 'nanjing'),
+ ('metro', 'ningbo', 'ningbo', 'ningbo'),
+ ('metro', 'sian', 'sian', 'xian'),
+ ('metro', 'urumchi', 'urumchi', 'wulumuqi'),
+]
+files = [(tuple(f"{archive}/{mode}/shapefiles/{d}/{city}_{mode}_{t}"
+ for t in ["stops_unique.dbf", "stops_unique.shp", "segments.dbf"]),
+ f"{py}_{mode}", None) for mode, d, city, py in cities]
+
+def fetch_upstream(force=False):
+ return fetch_upstream_files(__name__.split(".")[-1], upstream_prefix,
+ files, force)
+
+def read_dbf(data):
+ """The records of a dBase (DBF) file, as a list of dicts of strings."""
+ n, hlen, rlen = struct.unpack("<IHH", data[4:12])
+ fields = []
+ pos = 32
+ while data[pos] != 0x0D:
+ d = data[pos:pos + 32]
+ fields.append((d[:11].split(b"\0")[0].decode(), d[16]))
+ pos += 32
+ rows = []
+ for i in range(n):
+ r = data[hlen + i * rlen: hlen + (i + 1) * rlen]
+ if r[0:1] == b"*": # deleted record
+ continue
+ p = 1
+ row = {}
+ for fname, l in fields:
+ row[fname] = r[p:p + l].decode("utf8").strip()
+ p += l
+ rows.append(row)
+ return rows
+
+def read_shp_points(data):
+ """The (x, y) coordinates of the records of a shapefile of points."""
+ pos = 100
+ xy = []
+ while pos < len(data):
+ n, length = struct.unpack(">ii", data[pos:pos + 8])
+ stype = struct.unpack("<i", data[pos + 8:pos + 12])[0]
+ if stype not in (1, 11, 21): # Point, PointZ, PointM
+ raise ValueError(f"unexpected shape type: {stype}")
+ xy.append(struct.unpack("<dd", data[pos + 12:pos + 28]))
+ pos += 8 + 2 * length
+ return xy
+
+@cache_network()
+@coerce_props()
+@annotate()
+def parse(alts=None):
+ global files
+ name = __name__.split(".")[-1]
+ for (fnames, alt, fmt), (mode, d, city, py) in zip(files, cities):
+ if alts is not None and alt not in alts:
+ continue
+ data = []
+ for fname in fnames:
+ with open_upstream_file(name, fname, "rb") as f:
+ data.append(f.read())
+ stops = read_dbf(data[0])
+ xy = read_shp_points(data[1])
+ segments = read_dbf(data[2])
+ if len(xy) != len(stops):
+ raise ValueError("stops and their coordinates differ in number")
+ idx = {r["stop_id"]: i for i, r in enumerate(stops)}
+ if len(idx) != len(stops):
+ raise ValueError("repeated stop ids")
+ g = Graph(directed=True)
+ g.add_vertex(len(stops))
+ g.vp.name = g.new_vp("string", vals=[r["stop_cn"] for r in stops])
+ g.vp.name_en = g.new_vp("string", vals=[r["stop_en"] for r in stops])
+ g.vp.stop_id = g.new_vp("string", vals=[r["stop_id"] for r in stops])
+ g.vp.num_routes = g.new_vp("int", vals=[int(r["num"]) for r in stops])
+ g.vp.latitude = g.new_vp("double", vals=[p[1] for p in xy])
+ g.vp.longitude = g.new_vp("double", vals=[p[0] for p in xy])
+ g.add_edge_list([(idx[r["s_stopid"]], idx[r["e_stopid"]])
+ for r in segments])
+ g.ep.num_routes = g.new_ep("int", vals=[int(r["num"]) for r in segments])
+ g.ep.distance = g.new_ep("double", vals=[float(r["distance"])
+ for r in segments])
+ city_cn = set(r["city_cn"] for r in stops)
+ city_en = set(r["city_en"] for r in stops)
+ if len(city_cn) != 1 or len(city_en) != 1:
+ raise ValueError(f"several cities in {alt}: {city_cn} {city_en}")
+ g.gp.city_cn = g.new_gp("string", val=city_cn.pop())
+ g.gp.city_en = g.new_gp("string", val=city_en.pop())
+ g.gp.mode = g.new_gp("string", val=mode)
+ yield alt, g
+
+
+def get_pos(g, sbm_fit):
+ # Spatial network: laid out with the default parameters of cfd_layout(),
+ # without the partition of the SBM fit
+ return cfd_layout(g)