都民の森での新たな発見

秋空ひろがる 9 月の某日。

かつては毎週末ごとに訪れていた檜原村の都民の森にやってきました。

ここは新宿から片道 76km と、首都圏のまともなサイクリングスポットの中では(これでも)圧倒的に距離が近く、信号の数もおよそ 3km に1つと東京都内とは思えないほど常識的な範囲に収まっていることから、東京のサイクリストにとって、その名の通りに親しみやすいコースです。

決して悪いところではないのですが、それ以上でもそれ以下でもないので、積極的に東京を離れるようになってからは自然と足が遠のき、気がつけば数年のときが経過していました。

さきに述べたように東京都民にとっては極めて貴重なサイクリングスポットではあっても、普通に車も多くて走りづらさ満点ですし、輪行で訪れるにしてもアクセスが良いわけではありません。

もし自分がお隣の埼玉県や神奈川県に住んでいたとしたら、奥武蔵や伊豆箱根を差し置いて、わざわざ奥多摩を訪れることがあるだろうかと考えてしまいます。

ところが、実際に訪れてみると、こうした思い込みとはまた別の印象を抱きました。そして奥多摩自体がここ数年間で大きく変わっていることに驚きました。




ライドの出発地点はいつもの四谷見附 — と言いたいところですが、信号の待ち時間で日が暮れてしまうので、試験調査も兼ねて 5:29 の始発電車で新宿から高尾駅へと輪行します。

休日の東京の始発電車は登山客で満員になることも珍しくはありませんが、平日は平日で夜勤明けの帰宅需要がそれなりにあるらしく、始発の快速電車は意外にも混雑するという知見を得ました。

それから平日でも輪行する人はいるらしく、今回も先頭車両に乗っていると途中駅からミニベロを携えたお爺さんと遭遇し、高尾までご一緒することになりました。

高尾駅への到着は定刻通りの 6:23 — こちらはこちらで都心方面への通勤ラッシュが始まりそうな雰囲気で、駅前で輪行解除している傍から次々と人混みが押し寄せてきます。

人の少ない平日の休みを謳歌できるのは豊かな地方の話であって、こと首都圏においては当てはまらないのではないかと思えました。

車で混み合う高尾街道に沿って北上し、まずはウォーミングアップの和田峠に抜けます。

私にとって和田峠は峠原器みたいなもので、登った回数も桁違いに多いので、もはや何も感じなくなりました。

強いて感想をあげれば、山ガールって実在したのかといったところ。登坂中に大学生ぐらいの3人組を目撃し、その場違いな感じが強烈に印象に残りました。

そのまま和田峠を通過、甲武トンネルを経由して上川乗へと到着。

ここから真っ直ぐに目的地を目指すことも可能です。

しかし、補給食どころかドリンクすら用意してこなかったので、橘橋の檜原村役場のほうに寄り道してから都民の森を目指します。

走り出すと檜原村の微妙な変化に気づきます。

いつの間にか介護福祉施設や小さなスーパーマーケットが開業しています。

以前は村役場と都民の森のあいだの 20km 間には喫茶店と自動販売機しか存在せず、村内にはコンビニの1件すらなかったのに、今では食事処をいくつも選べるようになっています。

檜原村に活気がでてきているのは大きな変化ですが、走ってみると坂のキツさは相変わらずです。

と言うよりも、記憶のなかにあった都民の森の実走記録よりも、ずっとキツイです。

あらためて見ると登りが 20km もつづくルートなど、めったにないですし、ところどころで斜度 10% を超えてくる急勾配区間が地味に効いてきます。

料金所から先のいわゆる「ラスト 3km 区間」は易しいところで、本当に厳しいのは人里交差点、数馬ヘリポート、そして九頭龍神社の先の坂です。

この辺り、速く走ろうと重たいギアで突入すると、クランクが回せなくなります。

三頭山荘を通り過ぎて旧料金所にまで至れば、あとは 9% ぐらいで斜度が安定しているので、無事にここまで来れれば完走したようなものです。

余裕がでてきたところで、あたりを見渡すと舗装状態のよさに感心します。

登りが 20km も続くようなところも珍しいですが、その長さの道路をひび割れや落下物ひとつなく維持管理しつづけているのも、ここぐらいです。

台湾、九州から北海道までのあちこちの峠道を走ったあとで、あらためて訪れてみると奥多摩の特殊性が良く分かります。

ダウンヒルが苦手な人でも、安心して降れると断言できるぐらい綺麗な路面が奥多摩にはあります。

ただし、場所柄、非常に車が多いですし、速度取締重点路線として警察署に名指しされているぐらい暴走車両も少なくないので注意は必要です。

ここで少し道路から離れてみると、また新しい発見がありました。

ここまで自走で訪れて三頭山を登れという意味でしょうか。

ロードバイク × 登山という新しい可能性を感じます。

都民の森のある三頭山の反対側には、地味なことで有名な鶴峠がありますが、そのさらに向こうには絶景で名高い大菩薩嶺があることは登山家には有名です。

三頭山も登ってみると見どころの多い場所なのかも知れません。

都民の森をあとにして、風張峠をくだると奥多摩湖にたどり着きます。

この辺りは写真撮影には最高の場所です。山稜の緑とダム湖の青が作り上げる光景は関東でも有数の美しさを誇ります。

その一方で自転車で走行するには最低の場所です。

どの道路を通ってもトンネル続きで、交通量も檜原や都民の森とは比較にならないほど多いです。

しかもトンネル内は狭小で、照明もなく、湧水のせいで路面状態はまったく分からないのに落下物も多く、あげくに通行車両もダンプトラックを主体とした大型車ばかりという、考えられる限り最低の条件を合わせたような場所です。

これほどまでに好きで嫌いな場所というのは、ほかに国道 135 号ぐらいしか思い当たりません。

さすがに平日の朝なので、週末のように渋滞することはありませんけれども、関東らしい交通量の多さは相変わらずです。

そんな中でもロードバイクやクロスバイクの自転車乗りと3分間に1度ぐらい擦れ違います。

今まで知りませんでしたけど、平日に走りに出かけている人も多いのだなと実感できて新鮮な気分になりました。

そして、最後に梅ケ谷峠を超えて、京王八王子駅から輪行で帰ります。

ここは始発駅であり、スペースに余裕があることが多く(高尾駅との違い)、新宿まで乗換なしで40分前後で到着でき(拝島駅との違い)、さらに電車の本数自体も多い(奥多摩駅や武蔵五日市駅との違い)ので、奥多摩方面に向かうときには圧倒的に使いやすいです。

さらに混雑する時間は外しているはずなのですけれども、それでも新宿に近づくに連れて乗車人数が増えていくのは変わらずです。

最近、気がついたのですが郊外から都心に向かう電車では、途中下車する人がほとんどいないので、どれだけ空いているように見えても、結局、終点近くまで行けばいつでも混んでくることには違いがありません。

あらためて、はやく東京を離れたいと強く、強く思いました。

Garmin / Strava の走行記録をまとめて表示する

いままでに走ったことのある場所を一つの地図上にまとめて表示したい。自分と誰かの走行記録を一緒に表示したい。まだ走ったことのない場所を手早く見つけたい。

Garmin Edge といったサイクルコンピュータや Strava のような SNS を利用されている場合、今までのライドログを複数重ねて地図上にマッピングすることは簡単に実行できます。

必要な手順は大まかに3つぐらいです。

(1)座標データを集める
(2)座標データを Leaflet で描画する
(3)描画された地図(HTMLファイル)を表示する

一つづつ見ていきましょう。

(1)座標データを集める

Garmin デバイスの場合、スタートボタンを押して計測を開始してから保存を完了するまでのライド記録のすべてを FIT という形式のファイルに保存しています。

1つのファイルにライド1回分の情報があります。

この FIT ファイルは連携している Strava からもダウンロードすることが可能です。

この中には移動速度や気温やケイデンスなどのあらゆる計測データが記録されていますので、そこから座標データを抜き出して地図上に描画してやれば目的が達成されます。

ところが、ここで少し困ったことには FIT ファイルはバイナリファイルなので、テキストエディタなどで中身を覗こうとしても普通の人には読めません。


FITファイルの概要とFIT SDKのサンプルコード
https://qiita.com/harry0000/items/5931bbbf3d5b68fe8bc0


そこで一般的にはプログラムを使ってファイルから必要な情報を抜き出す処理を行います。




こうしたプログラムにはさまざまな種類がありまして、何を使ってもいいのですけれども、開発元からも公式にプログラムが配布されていますので、ひとまずはそれを使用することを考えます。


Downloads – THIS IS ANT
https://www.thisisant.com/developer/resources/downloads/


上のリンク先から FIT SDK という項目を選択すると Please Accept this Agreement Before Viewing This File という条件文が表示されますので、それを一読したあとに同意されるとファイルのダウンロードが行われます。

ダウンロードされた ZIP ファイルを解答すると java というディレクトリ(フォルダ)がありますので、その中にある FitCSVTool.jar というファイルが存在することを確認してください。

これは Java という言語で書かれたプログラムファイルです。プログラムを実行するには Java Runtime Environment という外部ソフトウェアが必要になります。

このプログラムファイルに正しく引数を与えて実行すると FIT ファイルがテキストファイルに変換されます。具体的にはこうやります。

$ java -jar FitCSVTool.jar -b activities/0000000000.fit outputFileName.csv

作成されたテキストファイルの中身には、以下のような記録が数十行から数千行に渡って並んでいます。

Data,12,record,timestamp,”933504317″,s,position_lat,”425645280″,semicircles,position_long,”1667343683″,semicircles,distance,”4978.48″,m,altitude,”56.60000000000002″,m,speed,”8.491″,m/s,unknown,”2710″,,unknown,”706″,,temperature,”27″,C,enhanced_altitude,”56.60000000000002″,m,enhanced_speed,”8.491″,m/s,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,

やるべきことは、ここから位置座標データを抜き取ることです。

目的の座標データは position_lat と position_long の直後に並んでいる数字列(太文字で強調している部分)です。

見た目は謎の数字列でしかありませんが、これを 2^31 (2の31乗)で割ってから 180 を掛けあわせると緯度と経度になります。

つまり、上の座標 (425645280,1667343683) とは 35°40’37.8″N 139°45’18.5″E のことです。

記憶容量の節約やプログラム処理の容易さなどの理由から32ビット整数で緯度経度を表現したら、こうなったのでしょうね。

対象となるファイルと目的が定まりましたら、対象の部分を抜き取って緯度経度に変換するという処理をスクリプトに落とし込み、作業をループで回して個々のファイルを処理していけば、必要な座標データは揃います。

一例をあげると、こんな感じに書けばやりたいことはできるかなと。

import com.garmin.fit.*;
import com.garmin.fit.csv.MesgCSVWriter;
import com.garmin.fit.csv.MesgFilter;

import java.io.File;
import java.io.FileInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.util.List;
import java.util.stream.Collectors;

public class Converter {

	public void convert(String dirPath) {
		try {
			for (File file : listFiles(dirPath)) {
				String outputFileName = file.getName().substring(0, 8);
				
				System.out.println(outputFileName);
				
				Decode decode = new Decode();
				MesgCSVWriter mesgWriter = new MesgCSVWriter(outputFileName + ".csv");
				FileInputStream fileInputStream = new FileInputStream(file);

				MesgFilter mesgFilter = new MesgFilter();
				mesgFilter.addListener((MesgDefinitionListener) mesgWriter);
				mesgFilter.addListener((MesgListener) mesgWriter);

				decode.addListener((MesgDefinitionListener) mesgFilter);
				decode.addListener((MesgListener) mesgFilter);
	
				while (fileInputStream.available() > 0) {
					decode.read((InputStream) fileInputStream);
					decode.nextFile();
				}
				mesgWriter.close();
			}
		} catch (IOException e) {
			e.printStackTrace();
		}
	}

	private List listFiles(String dirPath) throws IOException {
		return Files.walk(Paths.get(dirPath)).filter(Files::isRegularFile).map(Path::toFile)
				.collect(Collectors.toList());
	}

	public static void main(String args[]) {
		Converter Converter = new Converter();
		Converter.convert(args[0]);
	}
}

ただ、この段階で座標を変換しようとすると少し面倒だったので、出力される数値は生データのままです。

ここまで書かかなくても、わかる人はさっさと実行されていると思うのですけれども、反響(意味わかんねえよという苦情)が多ければ、ボタン操作で必要な情報を抜き出せるプログラムを公開するかもしれません。

作成すること自体は簡単なのですけれども、配布するためにはライセンスと利用規約を調べないといけないので、そこが面倒で時間がかかるのです。

座標データの準備ができましたら描画に移ります。

(2)座標データを Leaflet で描画する

データの準備ができましたら、走行経路を図形として地図上に描画します。

この目的で非常によく使われているライブラリに Leaflet.js というものがあります。Facebook でも Foursquare でも、どこでも使われているので、車輪の再発明を行うよりも使えるものは使ったほうがいいです。

ライブラリは CDN で配布されいるので HTML ファイルの最初にリンクを貼っておけば、ネットワークに接続されているコンピュータで利用できます。

使い方は単純なので公式のチュートリアルを見ながら自身で走行経路を描画されてもいいのですが、folium という Python のライブラリを使うと自動的に HTML ファイルとスクリプトを生成してくれるらしく、少し調べると先人の試行錯誤の記録がたくさん見つかります。

非常にありがたいことです。


Python + folium で Strava の "全"記録を地図で可視化 ? – hibomaの日記
https://hiboma.hatenadiary.jp/entry/2017/09/13/094056


Leaflet: Mapping Strava runs/polylines on Open Street Map · Mark Needham
https://markhneedham.com/blog/2017/04/29/leaflet-strava-polylines-osm/


ざっくりと見た感じでは、これを使うのが一番簡単そうに見えましたので、使ってみることにしますけれども、本音を述べると「なんだかなぁ」という気もします。

だって、普通の人のパソコンには Java も Python もインストールされてないじゃないですか(あと個人的にはあまり Python が好きではありません)。

取り敢えず、さきほど FIT ファイルから抜き出した座標データをそのまま使うのであれば、以下のように改編してやる必要があります。

#!/usr/bin/env python3
import os, sys, codecs, folium

def make(path, center):

    attributes='&copy; <a href="http://osm.org/copyright">OpenStreetMap</a> contributors'
    my_map = folium.Map(center, zoom_start=9,attr=attributes)
    
    folium.TileLayer('openstreetmap').add_to(my_map)
    folium.TileLayer('stamenterrain').add_to(my_map)
    folium.LayerControl().add_to(my_map)
    
    filelist = os.listdir(path)
    files = [file for file in filelist if file[-4:].lower() == ".csv"]
    for file in files:
        locations = []
        fin = codecs.open(path + "/" + file, "r", "utf-8")
        lines = fin.readlines()
        for line in lines:
            list = line.replace('"', '').strip().split(",")
            if list[0] != "Data" or list[6] != "position_lat" or list[9] != "position_long":
                continue
            try:
                lat = float(list[7]) * 180 / 2**31
                lon = float(list[10]) * 180 / 2**31              
                if isinstance(lat,float) and isinstance(lon,float):
                    locations.append(tuple([lat,lon]))
            except ValueError:
                continue
        if len(locations) > 1:        
            folium.PolyLine(locations=locations,color="red").add_to(my_map)
    my_map.save("./myroutes.html")

if __name__ == "__main__":
    dir_path = sys.argv[1]
    # center = sys.argv[2]
    center = [35.01848, 135.7799]
    make(dir_path, center)

各変数の内容を変えるとズームレベルや中心の座標、タイルの種類、走行経路の表示色を変更できます。

実行環境はこんな感じです。

$ python -V
Python 3.6.4 :: Anaconda, Inc.
$ pip -V
pip 19.2.3 from /home/buran/anaconda3/lib/python3.6/site-packages/pip (python 3.6)
$ python
>>> folium.__version__
'0.10.0'

(3)描画された地図(HTMLファイル)を表示する

folium で HTML ファイルを生成した場合には自動的にスクリプトファイルも読み込まれますので、このステップは必要ありません。

それ以外の場合には leaflet.js で地図を表示する HTML ファイルを作成し、そこに座標ファイルから作成された走行経路(具体的には polyline )を表示するスクリプトをあわせて読み込まないとなりません。

おそらく、こんな形になると思います。

<!DOCTYPE html>
<html>
<head>
  <title>My Ride Logs</title>
  <meta charset="utf-8" />
  <meta name="viewport" content="width=device-width, initial-scale=9.0">
  <link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/leaflet/1.5.1/leaflet.css" crossorigin="" />
  <script src="https://cdnjs.cloudflare.com/ajax/libs/leaflet/1.5.1/leaflet.js" crossorigin=""></script>
  <style>html, body {width: 100%;height: 100%;margin: 0;padding: 0;}</style>
  <style>#mapid {position:absolute;top:0;bottom:0;right:0;left:0;}</style>
  <meta name="viewport" content="width=device-width,initial-scale=1.0, maximum-scale=1.0, user-scalable=no" />
</head>
<body>
  <div id="mapid"></div>
  <script src="./myridelogs.js"></script>
</body>
</html>

詳しくは公式のチュートリアルを参照してください。

これに走行経路を描画するスクリプトを読み込ます。

ここでは myridelogs.js という名前のファイルを読み込んでいるので、これを再利用されるのであれば同じ名前のファイルを用意して HTML ファイルと同じディレクトリの中に入れて下さい。

L.tileLayer(
  'https://{s}.tile.openstreetmap.org/{z}/{x}/{y}.png', {
    maxZoom: 19,
    attribution: 'Map data © OpenStreetMap contributors, ' +
      'CC-BY-SA, ' +
      'Imagery © Mapbox',
    id: 'mapbox.streets'
  }).addTo(mymap);

var polyLine089 = L.polyline(
  [
    [48.12477, 11.56289],
    [48.12180, 11.56864],
    [48.12163, 11.56827],
    // 無駄に長いので以下略
  ], {
    "bubblingMouseEvents": true,
    "color": "green",
    "dashArray": null,
    "dashOffset": null,
    "fill": false,
    "fillColor": "red",
    "fillOpacity": 0.2,
    "fillRule": "evenodd",
    "lineCap": "round",
    "lineJoin": "round",
    "noClip": false,
    "opacity": 1.0,
    "smoothFactor": 1.0,
    "stroke": true,
    "weight": 3
  }
).addTo(mymap);

ここまでで用意できた HTML をウェブブラウザで読み込むと、サイクリングやランニングの走行記録を地図上に表示することができます。

やろうと思えば、サイクリングは青、ランニングは赤、スイミングは緑などと色を変えて同一の地図上に一緒に表示することも可能です。

やってること自体は単純なので、すぐにでも実現できるのではないかと思われますけれども、プログラムを自分で書いて実行しないといけないので、その部分は少し大変かも知れません。


追記:サイコンの FIT ファイルから位置情報を読み込んで地図上に線を引くアプリをつくりました

アプリを使ってライド記録を地図上に描こう

上海の自転車事情

中国発のイノベーションとして、一時期、話題になった自転車シェアリング。

日本や英国でもサービスを開始したと思いきや、わずか1年ほどで撤退してしまい、今では人々の記憶にすらも残っていないかも知れません。

App store に残されたレビューにも、サービスの継続やアプリの更新を疑問視する声が溢れています。

ところが、本場の中国においては自転車サイクリングは決して過去のものではなく、2019年現在においても街中の至るところで利用されています。

道路を走っているところを見かけるのは(自動)二輪車以外は、ほとんどがレンタルバイクと言ってもいいほど多くの利用者がおり、社会に定着している感があります。

※ 観察している限り、中国では二輪車と自転車との区分は明確ではないのか、同じ場所を一緒に走っていることが多いです。その一方で机动车(四輪車を含むエンジン付きの車) と非机动车(エンジン無しの自転車)の区別は明確です。




上海の道路は自転車で走りやすいところが多く、地形的に起伏が少ないことも影響しているかもしれません。

もちろん、上海の道路においても圧倒的に優遇されているのは四輪車なのですが(日本のように建前だけは歩行者優先ではなく完全に自動車が優先です)、郊外においては自転車と二輪車の専用レーンが設置されており、四輪車とも歩行者とも完全に分離されています。

加えて、驚くべきことに路上駐車が本当に少ないです。しばらく仕事で滞在していますが、最後に路上駐車している車を見たのはいつだか思い出せないほどです。

率直に述べて、東京と上海とで道路環境のみを比較した場合、東京のほうが上海よりも優れている点あるいは両者で均衡していると言える点など、ただの一つもありません。

かと言って、土地の所有権もない警察国家が羨ましいかどうかというのは、また別の話ではありますが。

通行者を容赦なく撮影する速度違反取締装置があり、絶対的な権力を持った警察が常時監視しているから、日本の道路を通行するよりも安全に感じるという一面もあるかもしれません。

それでも逆走や不法投棄などを少なからず目にするあたりに民族性を感じます。

都心部では自転車専用レーンがないところも一般的なので、歩道を走行している自転車や二輪車も見かけます。

どうやら歩行者専用道を除いては、自転車も二輪車も問題なく歩道を走行できるようです。

自転車が通行できない場所には明確に通行禁止の標識が掲げられていたり、禁止非机动车通行と警告表示がなされています。

さきに述べた繁華街の歩行者専用道などには、こうした標識が提示されています。

自転車を借りられることが分かり、交通ルールも覚えたら、実際にレンタルバイクに乗って散策してみたくなるものです。

中国の自転車シェアリングサービスを利用する場合には、現地で通信できるスマートフォンと WeChat もしくは Alipay が必要になります。

前者は SIM カードを現地調達すればいいとして、後者は少し厄介でして、インストールしたアプリに中国の銀行口座を紐付けないと使えないサービスもあるようです。

無事に準備が完了できたら、自転車に添付されているQRコードをスマートフォンの専用アプリで読み取って解錠します。

これだけを見ると簡単に利用できて便利に見えますが、実際には無人で管理されているがために、個々の自転車ごとに著しい状態差があったり、返却手続きが機能しなかったりと不便な点も少なからず感じられます。

レンタルバイクのなかには走行不能、もしくは、それに近いものも存在します。

かと言って、自分の自転車を持っていくと盗難の問題が付きまといますので、それはそれで不便な面もあります。

スポーツサイクルのショップも普通にありますけれども、路上でロードバイクやクロスバイクが走行しているところを見たことがありませんので、どれぐらい普及しているのかは不明です。

レンタルバイクに比較すると個人所有の自転車そのものが少ないので、安価でレンタルできる自転車をわざわざ購入する積極的な理由が乏しいのかも知れません。

私個人も写真撮影が捗らない、大気汚染が改善されてきているとはいえ臭いがまだ気になる、外国人がむやみに近づいてはならない場所が多すぎる、そもそもインターネット環境が不便すぎて情報検索すら一苦労などの諸々の事情から、あまり自分の自転車を持ってきたいとも思いません。

地下鉄を始めとする公共交通の料金もおそろしく安いので、都心部で生活する分には自転車がなくても不自由はしません。