Posted in: IT, Japan

日本的行政区划以及地图着色技术的使用

我们经常听到东京、大阪、横浜这些地名,它们针对的是什么行政区划?与日常生活的关系是什么?下面就以我生活了接近两年的经验来写一写。


(虚级)四个大岛

概述

全国分为北海道、本州、四国、九州四个大岛,这点我是在初中一年级的地理课上学到的。不知现在放在几年级了。

各个大岛包括周围的小岛就构成了相应的大区。另外还有一个冲绳,远离本土。

四个大岛(大区)示意图如右图所示。

面积排序以及与🇨🇳的省份对比如下:

面积(km2
🇨🇳广西237,606
🇯🇵本州227,942
🇨🇳湖南211,836
🇨🇳浙江105,506
🇯🇵北海道83,457
🇨🇳重庆82,374
🇨🇳宁夏51,893
🇯🇵九州36,782
🇨🇳海南35,177
🇯🇵四国18,803
🇨🇳北京16,406
🇨🇳上海8,359
🇯🇵冲绳2,282
🇨🇳香港1,111

人口排序以及与🇨🇳的省份对比如下:

人口(万)
🇨🇳广东12601
🇯🇵本州10397
🇨🇳山东10152
🇨🇳天津1386
🇯🇵九州1249
🇨🇳海南1008
🇨🇳青海592
🇯🇵北海道522
🇨🇳西藏364
🇯🇵四国356
🇯🇵冲绳146

与生活的联系:包邮区

显而易见,货物运往小岛比运往大岛困难。

  • 本州是最大的岛屿
  • 本州、四国、九州之间有公路(跨海大桥或海底隧道)连接
  • 北海道和冲绳是独立的岛屿,汽车需要借助轮渡开过去,类似于🇨🇳的海南岛到广东省的轮渡(春节期间经常上新闻的那种)。
  • 北海道与本州隔海相望,轮渡最近的距离是 17.5 km,与🇨🇳的海南岛到广东省的轮渡距离(22km)类似。
  • 冲绳与九州之间的直线距离达 500 km 以上

因此大部分网购的包邮(如果可以包邮)特点是:

  • 本州和四国地区基本都会包邮。
  • 对于大部分网购,九州地区也会包邮。
  • 送往北海道的,只有几个大的购物网站才会包邮。
  • 送往冲绳的,基本不会包邮,而且时间也不能保证。

(虚级)八大区域(都市圈)

概述

如上表所示,本州的面积和人口实在太大,不够“紧密”,有必要继续划分。所以在很多🇨🇳的地图网站上,你会看到本州岛上有4条线,把本州岛分成了5块区域。如右图所示。再加上北海道、四国、九州,全国就一共有八大区域。不过🇨🇳的地图来自于国家天地图,其中有个地方是错误的(下面 2.3 节里会提到)。

正确的分区在维基百科上有,如右图所示。即本州分为東北、関東、中部、近畿、中国五个部分,再加上北海道、四国、九州,一共八大区域。从面积和人口分布来看,接近于🇨🇳的“都市圈”。不过由于这个划分也是虚体性质(特别是“中部地区”有很多种不同的划分方案,见 2.2 节),因此这里就不再列表比较了。

复杂的“中部地区”

这个地区因为面积广大、群山阻隔,各个下属区域之间联系不紧密(经典例子:🇨🇳重庆市、陕西省汉中一带、河北省张家口一带、江苏的苏南苏北),因此还会继续划分,如下图所示。

左图中,红色区域是日本人口最多的关东地区。其他颜色就是上述复杂的“中部地区”。可以继续细分为东海、北陆、甲信等地区。我给甲信和新潟地区涂的颜色与关东接近,是因为它们与关东地区联系更加紧密,以至于经常一起合成为“关东甲信地区”(下文 2.5 节会提到在日常生活中的作用,这里有一个很大的坑。)

🇨🇳地图的错误

上文提到🇨🇳的地图来自于国家天地图,有个地方是错误的,这就是新潟区域。

关于该错误的原因,问 ChatGPT 没问出来。我猜测是:🇨🇳国家天地图的工作人员错误地把领事馆的领区范围和日本的行政区划混淆在一起了。“东北地方”包括青森、岩手、宮城、福島、山形、秋田六个县,最大的城市是宮城县的仙台市,居住中国人最多的也是仙台市。但奇怪的是中国的领事馆不设在仙台,却设在新潟,领区包括新潟、山形、福島、宮城四个县,完全不明所以🤔。

与生活的联系之一:看电视

在中国很少有人看地面波电视了,但是住在深圳的人如果经常去港式茶餐厅吃饭,就能看到餐厅的电视机里在播放香港🇭🇰的电视节目,这就是通过地面波实现的。地面波的接收是有范围限制的,在深圳、东莞能收到香港的信号,在远一些的惠州、河源就不行了。

在日本,地面波电视的信号接收范围就是以这八大区域为基础的。相同区域内的电视台内容相同,不同区域内的电视台就不同。不过现在愿意看电视的年轻人不多,这里就不详细写了。

与生活的联系之二:办绿卡(‼️)

前面 2.3 节在讲🇨🇳地图的错误时提到、中国驻日本领事馆的工作范围是和八大区域有点相关的(但不完全重合),同样的,在日本办理外国人相关的签证事务(日语:在留資格)也是和这八大区域相关,其中最重要的就是绿卡(永久居住权)的办理(日语:永住権)。

全日本只有以下 11 个绿卡审批点,基本上和八大地区相对应。

在留管理局的名称管辖范围
札幌北海道地区
仙台东北地区
东京关东地区、甲信地区⚠️、新潟⚠️
横滨关东地区的神奈川县
名古屋东海地区、北陆地区、
近畿地区的三重县⚠️
大阪近畿地区(不含三重县⚠️)
神户近畿地区的兵库县
广岛中国地区
高松四国地区
福冈九州地区(包括冲绳)
那霸冲绳

在这里重点提东京(管辖关东地区、甲信地区⚠️、新潟⚠️)和横滨(管辖神奈川县)两地,因为外国人(当然最多的就是🇨🇳人)众多,绿卡申请数量也多,因此审批速度十分缓慢。

与生活联系之三:网页上的地区导航页面

由于日本的第一级行政区有 47 个(下文 3.1 节所述),想在网页上做个地区分类导航就要搞 47 个链接,不方便。所以大部分网站会按照这种 8~10 个大区进行分类。当然各个网站会根据自己的业务特点对部分大区进行合并。例如著名的房地产导航网 SUUMO,首页的地区导航界面如下图所示:

从上图可以看出,因为是房地产业务,九州和冲绳合并了(后者业务体量小)。中部地区拆分为东海和北陆甲信地区。近畿地区则改名为“关西”以便于不熟悉的外国人也能寻找。

综上所述,想要尽快拿绿卡的,尽量避开住在关东地区、甲信地区⚠️和新潟⚠️。


第一级行政区:县(都道府県)(类比:🇨🇳地级市)

概述

都道府県(下文简称“县”,因为“县”占大多数)是日本的第一级行政区,实体的。一共47个。英语称为【Prefecture】。从面积和人口看可以类比为🇨🇳的地级市。除了北海道、冲绳这两个非包邮区之外,下面按照面积和人口列举三个极值以及和🇨🇳的地级市的对比。

🇯🇵面积最大的三个县(岩手、福岛、长野)、最小的三个县(东京、大阪、香川)以及与🇨🇳的地级市对比如下:

面积(km2
🇨🇳杭州市16,850
🇯🇵岩手县15,275
🇨🇳成都市14,334
🇯🇵福岛县13,784
🇨🇳大连市13,630
🇯🇵长野县13,561
🇨🇳沈阳市12,859
🇨🇳海口市2,284
🇯🇵东京都2,194
🇨🇳深圳市1,991
🇯🇵大阪府1,905
🇯🇵香川县1,876
🇨🇳厦门市1,699

🇯🇵人口最多的三个县(东京、神奈川、大阪)、最少的三个县(高知、島根、鳥取)以及与🇨🇳的地级市对比如下:

人口(万)
🇨🇳深圳市1756
🇯🇵东京都1404
🇨🇳杭州市1193
🇯🇵神奈川县923
🇨🇳沈阳市907
🇯🇵大阪府883
🇨🇳昆明市846
🇨🇳拉萨市86
🇯🇵高知县69
🇯🇵島根县67
🇯🇵鳥取县55
🇨🇳克拉玛依市49

众所周知中国的“地级市”(包括直辖市、副省级市等乱七八糟的东西)经常令人诟病的就是肆意扩大范围,比如北京市就包括渺无人烟的喇叭沟门之类的地方,哪怕是面积较小的深圳也包括一堆深山老林。同样地,这里的“一级行政区划”即包括繁华的市区也包括人烟稀少的山区海岛。有意思的是人口最多的东京都,即包含大家熟悉的银座、新宿、涉谷等繁华市区,也包括房子白送都没人要的多摩山区,还包括 1800km 远的南鸟岛(相对地,南鸟岛离美国关岛只有 1500 km)。不过不要紧,大部分情况下日常生活与一级行政区关系不大。目前我能想到的联系有以下两点:

与生活的联系之一:警察

地方警察是以一级行政区“县”为单位。“神奈川县警察”可能是日剧里面曝光频率最高的警车。你可能会说:我遵纪守法,不会经常跟警察打交道。但是别忘了交通也归警察管理,因此“驾照🪪事务”可能是唯一我们经常需要和警察打交道的业务。特别是要把🇨🇳驾照换成🇯🇵驾照时,一个县经常只有一个考点可以更换,哪怕你住的实际上离另一个县的考点更近,也不能跨县。

与生活的联系之二:住民税与部分福利

住民税就像是中国的“地税”、美国的“州税”,有收入的人需要向所居住的地区缴纳。有意思的是:住民税中有小部分是交给一级行政区的,大部分是交给二级行政区(下面 4.5 节提到)的。既然纳税就会有福利,因为交给一级行政区的税少,所以福利相对少。我遇到的主要一个福利,就是儿童免费医疗待遇,在相同的一级行政区(县)内可以直接结算,如果跨县,就需要先自己掏钱,然后跑去本地窗口申请,过很久才能到账。

还有就是儿童接种疫苗,这项内容不能跨县,必须在本县接种。

与生活的联系之三:网上填写地址时选择下拉菜单

如果在🇨🇳,网上填写地址时选择下拉菜单首先肯定是选择“省份”,而且首都北京放在第一位,后面按照华北、东北、华东、华中、华南、西南、西北、港澳这种顺序来排列,一共有34项。

在🇯🇵,因为“县”是一级行政区划,所以网上填写地址时选择下拉菜单首先就是选择“县”。不过有意思的是:

  1. 数量略多( 47 个)。
  2. 虽然“东京都”是日本中央政府所在地,可是一级行政区划的排序并不是把东京放第一位,而是把“北海道”放第一位,然后按照上面提到的八大区域里的東北、関東、中部、近畿、中国、四国、九州、沖縄的顺序排列。可是网民最多的是関東地区,関東地区中人口最多的“东京都”和“神奈川县”都排在后面。所以大部分网民如果想要在这个菜单里面选择的时候,必须反复下拉滚动条才行。

显然这样子会麻烦死。实际上大部分人在网上填写地址时,就算有这种菜单,也不会用。通常做法是:直接填入邮政编码,然后点击“根据邮政编码自动获取地址”,一下子就能补全所有的地址。与🇨🇳几乎不用邮政编码不同,在🇯🇵邮政编码是非常精确的。

综上所述,一级行政区划(都道府県)在面积和人口上接近于🇨🇳的地级市,但它与日常生活联系很小。


第二级行政区:市(市区町村)(类比:🇨🇳县级市、县、市辖区)

概述

在🇨🇳,县是市的下属。但在🇯🇵是反过来,市是县的下属。台湾🇹🇼模仿日本的行政区划也是如此。英语标记:市(包括东京的特别区) = City。町 = Town。村 = Village。很容易理解,最大的区别就是人口不同。二级行政区一共1741个。相比较而言人口多了10倍的🇨🇳,县级市+县+市辖区也不过2843个。

注意町和村不是市的下属。与🇨🇳的“镇”、“乡”、“村”完全不同。🇨🇳的“镇”或“乡”是“县级市”或“县”的下属,然后“村”又是“镇”或“乡”的下属。但是🇯🇵的市、町、村是平级的。人口 277 万的“大阪市”和人口 4500 人的“千早赤阪村”是平级的二级行政区,它们都归属于一级行政区“大阪府”。

没有东京市

我们熟悉的大阪、京都、横滨、札幌、名古屋、神戸等都是“市”,唯独不存在“东京市”(虽然历史上存在过)。“东京都”里面有二十三个“特别区”,每个都是标准的二级行政区,即与“大阪市”平级。

下面列举🇯🇵东京都(去掉了Town 和 Village)和🇨🇳天津市的二级行政区面积和人口对比。之所以选择🇨🇳天津为例子,是因为天津市和平区面积只有 9.98 km2,在🇨🇳想找到其他这么微型的行政区几乎不可能。即便如此,东京都仍然有三个市(羽村市、国立市、狛江市)的面积小于它。

一级区划二级区划面积(km2
🇨🇳天津市滨海新区2,389.20
🇨🇳天津市蓟州区1,589.36
🇨🇳天津市武清区1,571.68
🇨🇳天津市宝坻区1,509.71
🇨🇳天津市静海区1,474.03
🇨🇳天津市宁河区1,298.32
🇨🇳天津市西青区565.36
🇨🇳天津市北辰区475.75
🇨🇳天津市东丽区475.45
🇨🇳天津市津南区387.61
🇯🇵東京都八王子市186.38
🇯🇵東京都青梅市103.31
🇯🇵東京都あきる野市73.47
🇯🇵東京都町田市71.55
🇯🇵東京都大田区61.86
🇯🇵東京都世田谷区58.05
🇯🇵東京都足立区53.25
🇯🇵東京都江戸川区49.9
🇯🇵東京都練馬区48.08
🇯🇵東京都江東区42.99
🇨🇳天津市南开区40.64
🇨🇳天津市河东区39.63
🇨🇳天津市河西区38.01
🇯🇵東京都葛飾区34.8
🇯🇵東京都杉並区34.06
🇯🇵東京都板橋区32.22
🇨🇳天津市河北区29.62
🇯🇵東京都府中市29.43
🇯🇵東京都日野市27.55
🇯🇵東京都立川市24.36
🇯🇵東京都品川区22.85
🇯🇵東京都調布市21.58
🇨🇳天津市红桥区21.26
🇯🇵東京都多摩市21.01
🇯🇵東京都北区20.61
🇯🇵東京都小平市20.51
🇯🇵東京都港区20.36
🇯🇵東京都新宿区18.22
🇯🇵東京都稲城市17.97
🇯🇵東京都昭島市17.34
🇯🇵東京都東村山市17.14
🇯🇵東京都三鷹市16.42
🇯🇵東京都西東京市15.75
🇯🇵東京都中野区15.59
🇯🇵東京都武蔵村山市15.32
🇯🇵東京都渋谷区15.11
🇯🇵東京都目黒区14.67
🇯🇵東京都墨田区13.77
🇯🇵東京都東大和市13.42
🇯🇵東京都豊島区13.01
🇯🇵東京都東久留米市12.88
🇯🇵東京都千代田区11.66
🇯🇵東京都国分寺市11.46
🇯🇵東京都小金井市11.3
🇯🇵東京都文京区11.29
🇯🇵東京都武蔵野市10.98
🇯🇵東京都清瀬市10.23
🇯🇵東京都中央区10.21
🇯🇵東京都荒川区10.16
🇯🇵東京都福生市10.16
🇯🇵東京都台東区10.11
🇨🇳天津市和平区9.98
🇯🇵東京都羽村市9.9
🇯🇵東京都国立市8.15
🇯🇵東京都狛江市6.39
一级区划二级区划人口(万)
🇨🇳天津市滨海新区206.7 
🇨🇳天津市西青区119.5 
🇨🇳天津市武清区115.1 
🇯🇵東京都世田谷区93.8 
🇨🇳天津市津南区92.8 
🇨🇳天津市北辰区91.0 
🇨🇳天津市南开区89.0 
🇨🇳天津市河东区85.9 
🇨🇳天津市东丽区85.7 
🇨🇳天津市河西区82.2 
🇨🇳天津市蓟州区79.6 
🇨🇳天津市静海区78.7 
🇯🇵東京都練馬区75.0 
🇯🇵東京都大田区74.0 
🇨🇳天津市宝坻区72.2 
🇯🇵東京都足立区69.2 
🇯🇵東京都江戸川区69.1 
🇨🇳天津市河北区64.8 
🇯🇵東京都杉並区58.6 
🇯🇵東京都板橋区58.1 
🇯🇵東京都八王子市57.9 
🇯🇵東京都江東区52.4 
🇨🇳天津市红桥区48.3 
🇯🇵東京都葛飾区45.1 
🇯🇵東京都町田市43.2 
🇯🇵東京都品川区41.9 
🇨🇳天津市宁河区39.5 
🇨🇳天津市和平区35.5 
🇯🇵東京都北区35.3 
🇯🇵東京都新宿区34.6 
🇯🇵東京都中野区34.2 
🇯🇵東京都豊島区29.8 
🇯🇵東京都目黒区28.4 
🇯🇵東京都墨田区27.2 
🇯🇵東京都府中市26.3 
🇯🇵東京都港区25.8 
🇯🇵東京都調布市24.3 
🇯🇵東京都渋谷区24.2 
🇯🇵東京都文京区24.0 
🇯🇵東京都荒川区21.7 
🇯🇵東京都台東区21.2 
🇯🇵東京都西東京市20.7 
🇯🇵東京都小平市19.9 
🇯🇵東京都三鷹市19.6 
🇯🇵東京都日野市19.1 
🇯🇵東京都立川市18.4 
🇯🇵東京都中央区17.0 
🇯🇵東京都東村山市15.2 
🇯🇵東京都武蔵野市15.0 
🇯🇵東京都多摩市14.6 
🇯🇵東京都青梅市13.2 
🇯🇵東京都国分寺市13.1 
🇯🇵東京都小金井市12.7 
🇯🇵東京都東久留米市11.5 
🇯🇵東京都昭島市11.4 
🇯🇵東京都稲城市9.4 
🇯🇵東京都狛江市8.4 
🇯🇵東京都東大和市8.4 
🇯🇵東京都あきる野市7.9 
🇯🇵東京都国立市7.7 
🇯🇵東京都清瀬市7.6 
🇯🇵東京都武蔵村山市7.1 
🇯🇵東京都千代田区6.7 
🇯🇵東京都福生市5.6 
🇯🇵東京都羽村市5.4 

大城市的市辖区

上面 4.1 节提到,人口 277 万的“大阪市”和人口 4500 人的“千早赤阪村”是平级的二级行政区。但是一套行政班子要管理几百万人也够累的,于是大城市也会模仿【东京都】的区那样细分为一些“市辖区”。但是虽然汉字都写成【区】,本质上是不一样的,英语写法也不同。用下表就能看得很清楚。

一级行政区二级行政区三级行政区(虚)
東京都
Tokyo Prefecture
港区
Minato City
中央区
Chuo City
大阪府
Osaka Prefecture
大阪市
Osaka City
港区
Minato Ward1
中央区
Chuo Ward

東京都与大阪府平级(一级),東京都港区、東京都中央区与大阪市平级(二级)。而大阪市的港区和中央区是下属的虚体的行政区划。写成英文,東京的港区和中央区是两个不同的 City,市民福利不同(生活规则也不同,比如垃圾分类)。而大阪的港区和中央区属于相同的 City,市民福利相同。

选举市长

民主国家嘛自然得有政治权利,从上述 4.3 节东京和大阪的区别就能看出,居住在东京港区(Minato City)的居民选举的是港区的市长,而居住在大阪港区(Minato Ward,Osaka City)的居民选举的是大阪市的市长。从这里就能看出为什么没有【东京市】的本质原因了:二级行政区划是基层选举的基础单元。东京的 23 个区人口合计达到 960 万,是大阪的三倍以上。如果搞个【东京市】那就没法选举了,960万人根本无法了解市长候选人是否对自己有利。相比而言,东京港区人口是 25.8 万,东京中央区人口是 17 万,这样子选举就方便多了。

与生活的联系:各项福利的基础

上面 3-3 节 提到,住民税中只有一小部分是交给一级行政区(县)的,大部分是交给二级行政区(市)的,所以能享受的福利也是以二级行政区为主。一个典型的例子就是申请托儿所时是严格地按照二级行政区为分界线。哪怕隔壁市的托儿所离自己家更近也不行。

一二级行政区同名

在🇨🇳,地级行政区和县级行政区同名的以前很多、现在少了,典型的例子是【长沙市长沙县】、【南昌市南昌县】等。同样🇯🇵的一二级行政区也可以同名(只不过要反过来,【县】是上级,【市】是下级),最经典的当然是 4.3 节提到的【大阪府大阪市】,此外,京都、福冈、广岛、长崎都是类似的例子。当然再次提醒东京不是,因为不存在【东京市】。

町与村

4.1节 中提到町(Town)和村(Village)与市(City)的区别在于人口不同,自然是 City > Town > Village,但绝不意味着所有的 町(Town)和 村(Village)都很落后。就像🇨🇳的华西村(虽说现在已经没落了)一样,日本也有很多町和村很有名。例如🇯🇵第二大机场关西空港就不是在大阪市,1号和2号航站楼属于大阪府田尻町(人口仅8000多人),旅客在航站楼内的消费产生的税收全部归田尻町使用,爽歪歪。

🇹🇼台积电在🇯🇵熊本县开办的工厂也是位于一个叫做菊陽町的小镇,因为熊本县的机场位于此处,而不是位于熊本市。

地址相关

地图上的标记

以 Apple 地图为例,默认的“详细图层”上、用褐色字体表示一级行政区(例如千葉県静岡県等)(【东京都】除外,因为要表示成首都的格式,所以只写了“东京”两个字),同时用褐色虚线来表示它们的边界线。用黑色字体表示二级行政区。注意虽然字体有大有小,圆圈也有实心和空心之分,但它们本质上都是平级的二级行政区,只是人口不一样而已。

地址写法

日语的地址写法和中文类似,先写一级、再写二级、再写小区、门牌号等。特别大的城市可以省略一级行政区。🇨🇳典型的例子像义乌,不会写成【金华市义乌市】。同理,【大阪府大阪市】可以直接写【大阪市】,【神奈川县横滨市】可以直接写【横滨市】。英语地址则遵循国际规则,倒回来写,写完门牌号、小区后,再写二级、一级。

民用车牌号

在🇨🇳,车牌号开头首先是省份的简称,一个汉字对应一个省、严格一一对应的。然后用一个大写的英文字母来表示地级行政区(偶尔也有一个地级行政区使用 2 个字母的情况,但很罕见)。🇨🇳的车牌号能反映省级行政区 – 地级行政区的严格对应关系。

但在🇯🇵情况完全不同。🇯🇵的车牌号上面也会写地区的名字,但是这个地区是汽车管理局的名字,而不是车主所在的都道府県(一级行政区)或市区町村(二级行政区)的名字。一个汽车管理局管辖若干个相邻的市区町村(二级行政区),名字可能和所属的都道府県(一级行政区)同名,也可能和其中的某个市区町村(二级行政区)同名,也可能就是其他的名字。最著名的就是大阪的车牌。如下图:

电话归属地

在🇨🇳,固定电话有区号,一个区号对应一个地级行政区划,所以长期以来区号就成了城市的代称,以至于现在很多小城市为了附和大城市首先要采取“共用区号”策略,例如长沙、株洲、湘潭共用区号0731,好像不共用就显得自己不伟大似的。

在🇯🇵情况又是完全不同了,🇯🇵的固定电话号上确实也体现地区,但这个地区既不是都道府県(一级行政区)也不是市区町村(二级行政区),而是固定电话管理局的名字(参见 wiki 链接的 MA名)。如果 iPhone 开启“显示归属地”功能,显示的归属地就是这个 MA 名,一个 MA 管辖若干相邻区域,不一定严格按照二级行政区划分。名字可能是某个二级行政区的名字,也可能是一些已经废弃的老行政区名字。所以在🇯🇵,“显示归属地”这个功能基本上没什么用。

地图着色

概述

从上面 4.2 节🇨🇳天津市2和🇯🇵东京都的二级行政区对比可以看到,🇯🇵的二级行政区特点是数量多面积小,以至于一条地铁线穿过十几个市都是再普通不过的事情。可是🇯🇵市民的主要福利取决于二级行政区(4.5节),如果能在地图上用颜色区分不同的市就方便了。在🇨🇳这是比较难的,因为行政区边界被视为“国家机密”。但在🇯🇵这个数据是公开的,因此我可以尝试一下。下面的代码是 ChatGPT 辅助完成的。

六种颜色

数学上有个著名的“四色原理”,即只要使用四种不同的颜色,就可以给地图着色使得相邻行政区颜色不同。但是由于实际操作时有“飞地”的存在,需要稍微多一些颜色。ChatGPT 帮我选了容易区分的 6 种颜色:['#e6194b', '#3cb44b', '#ffe119', '#4363d8', '#f58231', '#911eb4']

各个行政区的边界坐标数据

来自 nlftp.mlit.go.jp,每年都会更新。写本文时最新的数据是2024年1月1日。我喜欢里面的 GEOJSON 格式,虽然文件较大(453MB),但是看着清楚。GEOJSON 的每个 feature 代表一个二级行政区,有以下几项:

含义值的示例
N03_001都道府県名(一级行政区划)北海道
N03_002北海道の振興局名オホーツク総合振興局
N03_003郡名東茨城郡
N03_004市区町村名(二级行政区划)大阪市
N03_005政令指定都市の行政区域名中央区(大阪市)
N03_007全国地方公共団体コード01233(五位数字编号)

其中有用的是 N03_0013.1节)、N03_0044.1节)、N03_0054.3节,虽然是虚级的,但也划分出来了,否则有些大城市太大不好看。) 和 N03_007N03_007这个键是精准的指标,可以视为“索引”或者“主键”(毕竟名字可能重名),但是在部分地区有可能为缺失值(null),跳过这些区域即可。

给各个行政区染色

该段代码是 ChatGPT 完成的,目的是给 GEOJSON 中的每个 feature 添加一个 color 属性。下面这段代码输入文件是 input.geojson,输出文件是 output.geojson

import json
import networkx as nx
from shapely.geometry import shape, Polygon
from rtree import index

def load_geojson(filename):
    with open(filename, 'r') as f:
        data = json.load(f)
    return data

def build_adjacency_graph(features):
    graph = nx.Graph()
    polygons = {}
    idx = index.Index()

    for i, feature in enumerate(features):
        properties = feature['properties']
        node = properties['N03_007']
        if node is None:
            continue
        polygon = shape(feature['geometry'])
        polygons[i] = (node, polygon)
        idx.insert(i, polygon.bounds)
        graph.add_node(node)

    for i in polygons:
        node1, polygon1 = polygons[i]
        for j in idx.intersection(polygon1.bounds):
            if i != j:
                node2, polygon2 = polygons[j]
                if polygon1.touches(polygon2):
                    graph.add_edge(node1, node2)
    
    return graph

def greedy_coloring(graph, color_palette):
    color_map = {}
    num_colors = len(color_palette)
    for node in graph.nodes:
        used_colors = {color_map[neighbor] for neighbor in graph.neighbors(node) if neighbor in color_map}
        for color in color_palette:
            if color not in used_colors:
                color_map[node] = color
                break
        else:
            raise ValueError("More colors are needed than provided in the palette.")
    return color_map

def assign_colors_to_features(features, color_map):
    for feature in features:
        properties = feature['properties']
        node = properties['N03_007']
        if node is None:
            continue
        color = color_map[node]
        properties['color'] = color

def save_colored_geojson(data, output_filename):
    with open(output_filename, 'w') as f:
        json.dump(data, f, ensure_ascii=False)

if __name__ == "__main__":
    input_filename = "input.geojson"
    output_filename = "output.geojson"
    
    # Define the color palette
    color_palette = ['#e6194b', '#3cb44b', '#ffe119', '#4363d8', '#f58231', '#911eb4']

    data = load_geojson(input_filename)
    features = data['features']
    graph = build_adjacency_graph(features)
    color_map = greedy_coloring(graph, color_palette)
    assign_colors_to_features(features, color_map)
    save_colored_geojson(data, output_filename)

对于本次的 GEOJSON 数据(500MB),运行时间大约 26 分钟左右。

转换为 mbtiles 格式

原始 GEOJSON 和转换之后的 GEOJSON 均达到 500 多 MB。这么大的文件不可能直接加载,我想到了使用 MapBox 的自定义 Tilesets。可是它最多也只能上传 300MB 大小的文件,所以需要先转换为较小的 mbtiles 格式文件,再上传。

把 GEOJSON 转换为 mbtiles 需要使用 tippecanoe 这个工具。在 macOS 上用 homebrew 安装比较容易,转换方法是:

# 输入的 in.geojson 文件可以是 gzip 压缩格式
tippecanoe -zg --drop-densest-as-needed -o out.mbtiles in.geojson.gz

转换之后的 mbtiles 文件只有 55MB 左右,为原来的十分之一。在我的 macOS (M1 芯片) 上所需时间为 2 分钟左右。

用 mapbox GL JS 渲染

下面的代码也是 ChatGPT 辅助的。

mapboxgl.accessToken = 'my.token'
const map = new mapboxgl.Map({
        container: 'map',
        projection: 'globe',
        style: `mapbox://styles/mapbox/streets-v12`, // style URL
        center: [132, 34], // starting position [lng, lat]
        zoom: 10,
});
map.addControl(new MapboxLanguage({
        defaultLanguage: 'ja'
}));
var popup = new mapboxgl.Popup({
        closeButton: false,
        closeOnClick: true,
        closeOnMove: true
});
map.on('load', () =>{
        map.addSource('geo-region', {
                type: 'vector',
                url: "mapbox://myname.myid"
        });
        map.addLayer({
                'id': "geo-border",
                'type': "line",
                'source': 'geo-region',
                'source-layer': 'my.layer.name',
                'paint': {
                        "line-color": "black",
                        "line-width": 1
                }
        });
        map.addLayer({
                'id': "geo-region",
                'type': "fill",
                'source': 'geo-region',
                'source-layer': 'my.layer.name',
                'paint': {
                        'fill-color': [
                                'case',
                                ['has', 'color'], ['get', 'color'],
                                'rgba(0, 0, 0, 0)' // Transparent color for features without 'color' property
                        ],
                        'fill-opacity': [
                                'case',
                                ['boolean', ['feature-state', 'hover'], false],
                                0.5,
                                ['boolean', ['feature-state', 'click'], false],
                                0.5,
                                0.2
                        ]
                }
        });
        let hoveredStateId = null;
        map.on('mousemove', 'geo-region', function(e) {
                map.getCanvas().style.cursor = 'pointer';
                var feature = e.features[0];
                popup.setLngLat(e.lngLat).setHTML(`${feature.properties.N03_001}/${feature.properties.N03_004}`).addTo(map);

        });
        map.on('mouseleave', 'geo-region', function() {
                map.getCanvas().style.cursor = '';
                popup.remove();
        });
});

最后渲染效果如下图所示。地图上添加了一个透明的涂着不同颜色的图层用来区分不同的二级行政区。点击可以弹出窗口显示1、2级行政区。

  1. 东京以外的大城市的“区”,英语可以写成“Ward”也可以写成“Ku”,都可以 ↩︎
  2. 近年来随着🇨🇳各大城市兴起“并区”热潮,例如北京的宣武区与西城区合并、上海的静安区和闸北区合并,县级行政区划越来越少,单个行政区面积人口越来越大。像天津这种微型的行政区划已经很罕见了。 ↩︎

Comments (7) on "日本的行政区划以及地图着色技术的使用"

    1. Safari 17.5 Mac OS X  10.15.7

      倒不至于和西伯利亚一样,人口除以面积,北海道的人口密度是每km2有62人,与黑龙江省差不多,高于甘肃。

  1. Google Chrome 124.0.0.0 Mac OS X  10.15.7

    哇,这一篇写的很用心,有点像知乎遗风的风格,可惜知识盲区太多,看不大细。东京居然不是市,也就是它没有自己的市政府,而是由很多区和独立市组成的一个“首都圈”?跟粤港澳大湾区差不多?长知识了。

    1. Safari 17.5 Mac OS X  10.15.7

      东京下面的每个区就是一个单独的市,英文翻译成 City,有政府。它负责具体的市民事务。

发表回复

您的邮箱地址不会被公开。 必填项已用 * 标注