Leafletで地図を作ろう - (東海道五十三次ルートマップ)

1.東海道五十三次ルートマップ

作品例として、東海道五十三次ルートマップを作成します。この題材は歌川広重の東海道五十三次の浮世絵を利用して 江戸(日本橋)から京都(三条大橋)までの487.8キロメートルのルートを辿っていきます。起点から終点までの間の宿場地点に 1番から55番までのアイコン画像マーカーを配置しています。マーカーをクリックするとポップアップに広重の浮世絵画像と宿場名や距離などを 表示します。ソースコードの大部分は、すでに公開している 「気象庁震度観測点マップ」からの流用です。内容が重複するので「気象庁震度観測点マップ」で説明していない追加、変更部分のみを中心に 説明していきます。
浮世絵の画像素材は、下記サイト様にて提供されているものを利用させて頂きました。

取得先 : 浮世絵著作権フリー作品「東海道五十三次」

作品例のポイントとなる部分は、日本橋から京都までの約500キロメートルのルート線データの作成方法です。地図上の旧東海道を探しながら 線と線を繋いでいきます。線を繋ぐ位置の座標(緯度・経度)を取得して指定いかなければなりません。作成方法についても詳しく説明します。


2.ソースコードの説明

東海道五十三次ルートマップのメインとなる(toukaidou.html)コードです。各実装部品の定義ファイル類は外部ファイルとしています。 leaflet.css、all.css、toukaidou.css、leaflet.js、SlideMenu.js、toukaidou.js、toukaidef.js、route531.js、route532.jsライブラリ等をヘッダー部分に指定しています。

toukaidou.cssとtoukaidou.js、toukaidef.js、route531.js、route532.jsは筆者が作成したものです。

DEMO

 //***** 宿場名配列 *****
   var selbox= [
       { val:"",name:"宿場名"},{val:"0",name:"01.日本橋 (にほんばし)"},{val:"1",name:"02.品川宿 (しながわ)"},{val:"2",name:"03.川崎宿 (かわさき)"},
       { val:"3",name:"04.神奈川宿 (かながわ)"},{val:"4",name:"05.保土ヶ谷宿 (ほどがや)"},{val:"5",name:"06.戸塚宿 (とつか)"},{val:"6",name:"07.藤沢宿 (ふじさわ)"},
       { val:"7",name:"08.平塚宿 (ひらつか)"},{val:"8",name:"09.大磯宿 (おおいそ)"},{val:"9",name:"10.小田原宿 (おだわら)"},{val:"10",name:"11.箱根宿 (はこね)"},
       { val:"11",name:"12.三島宿 (みしま)"},{val:"12",name:"13.沼津宿 (ぬまづ)"},{val:"13",name:"14.原宿 (はら)"},{val:"14",name:"15.吉原宿 (よしわら)"},
       { val:"15",name:"16.蒲原宿 (かんばら)"},{val:"16",name:"17.由比宿 (ゆい)"},{val:"17",name:"18.興津宿 (おきつ)"},{val:"18",name:"19.江尻宿 (えじり)"},
       { val:"19",name:"20.府中宿 (ふちゅう)"},{val:"20",name:"21.鞠子宿 (まりこ)"},{val:"21",name:"22.岡部宿 (おかべ)"},{val:"22",name:"23.藤枝宿 (ふじえだ)"},
       { val:"23",name:"24.島田宿 (しまだ)"},{val:"24",name:"25.金谷宿 (かなや)"},{val:"25",name:"26.日坂宿 (にっさか)"},{val:"26",name:"27.掛川宿 (かけがわ)"},
       { val:"27",name:"28.袋井宿 (ふくろい)"},{val:"28",name:"29.見付宿 (みつけ)"},{val:"29",name:"30.浜松宿 (はままつ)"},{val:"30",name:"31.舞坂宿 (まいさか)"},
       { val:"31",name:"32.新居宿 (あらい)"},{val:"32",name:"33.白須賀宿 (しらすか)"},{val:"33",name:"34.二川宿 (ふたかわ)"},{val:"34",name:"35.吉田宿 (よしだ)"},
       { val:"35",name:"36.御油宿 (ごゆ)"},{val:"36",name:"37.赤坂宿 (あかさか)"},{val:"37",name:"38.藤川宿 (ふじかわ)"},{val:"38",name:"39.岡崎宿 (おかざき)"},
       { val:"39",name:"40.池鯉鮒宿 (ちりゅう)"},{val:"40",name:"41.鳴海宿 (なるみ)"},{val:"41",name:"42.宮宿 (みや)"},{val:"42",name:"43.桑名宿 (くわな)"},
       { val:"43",name:"44.四日市宿 (よっかいち)"},{val:"44",name:"45石薬師宿 (いしやくし)"},{val:"45",name:"46.庄野宿 (しょうの)"},{val:"46",name:"47.亀山宿 (かめやま)"},
       { val:"43",name:"48.関宿 (せき)"},{val:"48",name:"49.坂下宿 (さかのした)"},{val:"49",name:"50.土山宿 (つちやま)"},{val:"50",name:"51.水口宿 (みなくち)"},
       { val:"51",name:"52.石部宿 (いしべ)"},{val:"52",name:"53.草津宿 (くさつ)"},{val:"53",name:"54.大津宿 (おおつ)"},{val:"54",name:"55.京都三条大橋 (きょうと)"}
   ];

   //***** 凡例:色定義 (地域別)*****
   var colorDefs = [
       { title: '東京都    : (~品川) (1)', color: '#ff5891' },
       { title: '神奈川県  : (川崎~箱根) (9)', color: '#0061c1' },
       { title: '静岡県    : (三島~白須賀) (22)', color: '#1a883a' },
       { title: '愛知県    : (二川~宮 ) (9)', color: '#ff7f27' },
       { title: '三重県    : (桑名~坂下) (7)', color: '#a349a4' },
       { title: '滋賀県    : (土山~大津) (5)', color: '#840b11' },
       { title: '合計: (53箇所)', color: '#ffffff' },
       { title: '(*注)出発点の日本橋と終点の', color: '#ffffff' },
       { title: '三条大橋は除く。', color: '#ffffff' }
   ];

  //***** 簡易マイ・メニュー定義( SlideMenu) *****
  var SlideMenu_ma = [
       { title: '東海道五十三次 - Wikipedia', url: 'https://ja.wikipedia.org/wiki/東海道五十三次',img: '"./image/ball3.png" width=8 height=8 align="absmiddle"' },
       { title: '東海道五十三次 (浮世絵)', url: 'https://ja.wikipedia.org/wiki/東海道五十三次 (浮世絵)',img: '"./image/ball3.png" width=8 height=8 align="absmiddle"' },
       { title: '東海道 - Wikipedia', url: 'https://ja.wikipedia.org/wiki/東海道',img: '"./image/ball3.png" width=8 height=8 align="absmiddle"' }
   ];
  //***** SlideMenu-2 *****
  var SlideMenu_mb = [
      { title: '東海道五十三次距離換算表', url: './html/toukaidou01.html',img: '"./image/ball5.png" width=8 height=8 align="absmiddle"' },
      { title: '東海道五十三次宿場の概要', url: './html/toukaidou02.html',img: '"./image/ball5.png" width=8 height=8 align="absmiddle"' }
   ];

    // ****** 背景色変更 ******
    var obj,evobj;
    function bgrecover(bg) { evobj.style.backgroundColor=bg; }
    function setColor(obj,col) { obj.style.backgroundColor = col; }
    function ResetColor(obj,col) { obj.style.backgroundColor = col; }

    // ****** 'table' , 'tr'タグ部分を避ける制御  *****
    function cellbgcolor(bg) {
        evobj=event.srcElement;
        if(evobj.tagName=="TABLE") {
           return
        }
        while(evobj.tagName!="TR") {
              evobj=evobj.parentElement;
        }
        evobj.style.backgroundColor=bg;
    }

    // ****** 別のHTMLに切替 (target="_blank") ******
    function JumpURL(URL) {
        window.open(URL,'_blank',"directories=no,location=yes,menubar=yes,resizable=yes,scrollbars=yes,status=yes,toolbar=yes");
    }
a          { font-size: 12px;text-decoration:none;font-family:Meiryo UI; }
a:link     { color:#0033aa; }
a:hover    { color:#aa0000; }
a:visited  { color:#0000ff; }
body       { font-size: 12px;font-family:Meiryo;
            margin:  0;
            padding: 0;
}
.info {
            padding: 10px 10.5px; 
            background: white;
            border-radius: 3px;
} 
.legend-item {
            display: flex;
            font-family : Meiryo UI;
            font-size: 10.5px;
            color: #555555;
            width: 165px;
            align-items: center;
}
.legend-item-color {
            display: block;
            width: 15px; 
            height: 15px;
            margin-right: 5px; 
}
.cstm-slidmenu-header {
            display: block;
            margin: 0;
            font-size: 14px;
            font-weight: bold;
            width: 180px;
            color: #ffffff;
            text-align : center;
}
.menustr {
            font-size: 11px;
            font-family: Meiryo UI;
            padding-left: 6px;
            color: #ffffff;
            cursor: pointer;
}
.combinfo-set {
            font-size: 11px;
            font-family:Meiryo;
            color: #000000;
            width: 180px;
            height: 20px;
            margin-top: 1px;
            margin-bottom: 1px;
            background: #fefcc8;
            cursor: pointer;
}

2-1.ルート線の描画

スライドメニュー、コンボボックス、凡例、画像、アイコンマーカー、スケール等のコード部分は、 「気象庁震度観測点マップ」と「都道府県別人口統計マップ」にて説明していますので参照願います。 ここでは、この作品のメインとなるルート線の描画部分です。元となるルート線データを作成していれば 描画はいたって簡単です。 route.addTo(map);とするだけです。実際は、宮宿~桑名宿間は海路となるので除く 必要があるためルートデータを2つに分割しています。

      //***** 江戸日本橋~京都三条橋までのルートを描画(宮宿~桑名宿間を除く) *****
      route1.addTo(map);
      route2.addTo(map);
      //***** 江戸日本橋~京都三条橋までのルートを描画 *****
      var route1 = L.polyline([
           [35.684463, 139.774605],[35.684195, 139.774514],[35.683975, 139.774466],[35.683757, 139.774407],
           [35.683435, 139.774246],[35.675208, 139.769117],[35.674955, 139.768932],[35.667664, 139.761301],
           [35.667303, 139.760910],[35.660425, 139.755212],[35.660164, 139.755105],[35.651254, 139.753882],
           [35.649114, 139.753088],[35.649036, 139.753001],[35.648988, 139.752902],[35.648914, 139.752772],
           [35.648116, 139.749934],[35.646869, 139.747214],[35.646337, 139.746115],[35.646115, 139.745707],


           ~ 中略 ~


           [35.067773, 136.965678],[35.068515, 136.964611],[35.068788, 136.964235],[35.068946, 136.964015],
           [35.069143, 136.963608],[35.069376, 136.963077],[35.070034, 136.961049],[35.070632, 136.959241],
           [35.071396, 136.957047],[35.071659, 136.956258],[35.072006, 136.955454],[35.072159, 136.955159],
           [35.072963, 136.953973],[35.074521, 136.951693],[35.075685, 136.950004],[35.076572, 136.948738],
           [35.076857, 136.948249],[35.077933, 136.945776],[35.080259, 136.940439],[35.080378, 136.940176],
           [35.080637, 136.939800],[35.080896, 136.939505],[35.082419, 136.937939],[35.082849, 136.937445],
           [35.084570, 136.934516],[35.087581, 136.932853],[35.104778, 136.923648],[35.105024, 136.923584],
           [35.115099, 136.921942],[35.116346, 136.918627],[35.116302, 136.918445],[35.116732, 136.916857],
           [35.116868, 136.916336],[35.117188, 136.915360],[35.117197, 136.914990],[35.117144, 136.914582],
           [35.117179, 136.914164],[35.117250, 136.913960],[35.118004, 136.912597],[35.119338, 136.911396],
           [35.119637, 136.911063],[35.119873, 136.910591],[35.120194, 136.909888],[35.121137, 136.907828],
           [35.120817, 136.907582],[35.120773, 136.907421],[35.120067, 136.907448],[35.120040, 136.907163]
      ], {color: "blue", weight: 5});
      //***** 江戸日本橋~京都三条橋までのルートを描画 *****
      var route2 = L.polyline([
           [35.078416, 136.686391],[35.078249, 136.686128],[35.078056, 136.685854],[35.077845, 136.685602],
           [35.077559, 136.685328],[35.077419, 136.685227],[35.077138, 136.685076],[35.076861, 136.684937],
           [35.076677, 136.684873],[35.076286, 136.684808],[35.075939, 136.684797],[35.075544, 136.684856],
           [35.075263, 136.684926],[35.073073, 136.685951],[35.072195, 136.686375],[35.071729, 136.686632],
           [35.070228, 136.687319],[35.069938, 136.687426],[35.069947, 136.687415],[35.069753, 136.687453],


           ~ 中略 ~

           [35.001114, 135.792807],[35.002019, 135.792212],[35.002784, 135.791847],[35.002995, 135.791729],
           [35.003162, 135.791584],[35.003522, 135.791128],[35.003834, 135.790871],[35.003996, 135.790785],
           [35.004225, 135.790753],[35.004405, 135.790785],[35.004801, 135.790924],[35.005200, 135.791059],
           [35.005512, 135.791101],[35.005732, 135.791059],[35.005921, 135.791016],[35.006633, 135.790565],
           [35.006967, 135.790372],[35.007208, 135.790313],[35.007630, 135.790243],[35.007907, 135.790216],
           [35.008219, 135.790125],[35.008579, 135.789884],[35.008817, 135.789696],[35.008948, 135.789583],
           [35.009032, 135.789412],[35.009120, 135.789240],[35.009203, 135.788934],[35.009296, 135.788607],
           [35.009339, 135.788221],[35.009550, 135.785812],[35.009550, 135.785549],[35.009296, 135.782792],
           [35.009234, 135.782309],[35.009256, 135.781226],[35.009296, 135.780711],[35.009427, 135.779552],
           [35.009449, 135.779235],[35.009458, 135.778930],[35.009454, 135.778082],[35.009436, 135.776639],
           [35.009419, 135.776070],[35.009388, 135.775448],[35.009252, 135.773903],[35.009212, 135.773453],
           [35.009159, 135.773200],[35.009098, 135.772262]
      ], {color: "blue", weight: 5});

2-2.アイコンマーカーの配置とポップアップ

マーカーの配置やポップアップのコード部分は、「気象庁震度観測点マップ」と同じです。コードの説明は「気象庁震度観測点マップ」を参照して下さい。 大量のアイコンマーカーの配置とポップアップ(吹き出し)を指定した座標(緯度、経度)の位置に展開します。 種類の違う複数の任意アイコン画像のマーカーを指定した位置に配置するために外部ファイルとしてtoukaidou.jsとして作成します。 toukaidou.jsも「気象庁震度観測点マップ」の構成と相違ありません。データ内容が違うだけです。

※リスト表示にsyntaxhighlighterを利用していますが、スクリプト中に<br>タグを含んでいる 箇所が何箇所かあり、toukaidou.jsコード表示が正しく表示されないため掲載していません。コードは一部分のみのサンプルですが、下記から確認して下さい。

toukaidou.jsファイル確認


3.ルート線描画データの作成方法

ルート線データの作成方法について説明します。結論から言えば地道に緯度と経度を取得してデータ化する以外に良い方法 はないと思います。自動的に効率良く作成出来れば良いのですが、難しいようですね。今回は東海道五十三次で推定ルートマップの作成を 実際に行った方法について紹介します。作業としては、地図上の指定した場所をクリックしたら緯度と経度を取得できなけばなりません。 クリックした座標位置の緯度と経度の表示とルート線を描画する簡単なツールを作成しました。clickevent.htmlをブラウザから起動します。
シングルクリックイベントを実装するコード(singleclick.js)は、GitHubからダウンロードして、ソースコードをHTML内に貼り付けて利用しました。

取得先: singleclick.jsの本体ダウンロード(GitHub)

[ データ作成手順 ]
    (1)clickevent.htmlをメモ帳などのテキストエディタで開く。
    (2)clickevent.htmlをブラウザから起動する。
    (3)テキストエディタで43行目の var mpoint = [緯度, 経度]; のビューポート位置を確認
        しておく。
    (4)マウスクリックして緯度、経度を取得。ポップアップが表示されるので座標を
        コピーする。
    (5)コピーした座標をソースコードの64行目から貼り付けていき上書き保存する。
    (6)(4)から(5)までの作業を繰り返す。
    (7)ブラウザの描画を更新する。64行目から追記したルートデータが地図上に反映される。
    (8)地図上のビューポートを移動していきたいので、(3)のmpointの緯度、経度を最後に
        取得した座標に設定する。
    (9)終点位置まで上記の作業手順を繰り返し実行する。
      //***** 座標の指定 (仮に中央区日本橋の起点付近に設定してある : 任意に変更可) *****
      var mpoint = [35.684468, 139.774606];
      var map = L.map('mapcontainer').setView(mpoint, 18);
      //***** 取得したルートデータ(緯度、経度)を追記していく *****
      var route = L.polyline([
           [35.684463, 139.774605],[35.684195, 139.774514],[35.683975, 139.774466], ....





      ], {color: "blue", weight: 5});
L.polylineでは、線色や線の太さを変更できるので、ソースコードの70行目の{color: "blue", weight: 5}部分を変更して好みのものにして下さい。
      ], {color: "blue", weight: 5});
      //***** ルート線の描画 *****
      route.addTo(map);
本作品例の東海道五十三次のデータ作成は、日本橋から京都までデータ化するのに約14日間ほど作業日数がかかりました。 旧東海道が途中で途切れていたり、ルートがわからなかったりして調べたり推定する部分もあり、かなり大変な作業でした。

DEMO
<!DOCTYPE html>
<html lang="ja">
<head>
  <meta charset="UTF-8">
  <meta http-equiv="X-UA-Compatible" content="ie=edge">
  <title>クリックした位置の緯度経度を取得</title>
 <link rel="stylesheet" href="https://unpkg.com/leaflet@1.4.0/dist/leaflet.css" />
  <script src="https://unpkg.com/leaflet@1.4.0/dist/leaflet.js"></script>
  <script>
   //***** シングルクリックイベント (Leaflet.singleclick.jsより転記) *****
    L.Evented.addInitHook(function() {
        this._singleClickTimeout = null;
        this.on('click', this._scheduleSingleClick, this);
        this.on('dblclick dragstart zoomstart', this._cancelSingleClick, this);
    });
    L.Evented.include({
        _cancelSingleClick : function() {
            setTimeout(this._clearSingleClickTimeout.bind(this), 0);
       },
       _scheduleSingleClick: function(e) {
            this._clearSingleClickTimeout();
            this._singleClickTimeout = setTimeout(this._fireSingleClick.bind(this, e), (this.options.singleClickTimeout || 500));
       },
        _fireSingleClick: function(e) {
            if (!e.originalEvent._stopped)
           {
               this.fire('singleclick', L.Util.extend(e, { 
                   type : 'singleclick'
               }));
           }
       },
        _clearSingleClickTimeout: function() {
            if (this._singleClickTimeout != null) 
           {
               clearTimeout(this._singleClickTimeout);
               this._singleClickTimeout = null;
           }
       }
    });

    function init(){
      //***** 座標の指定 (仮に中央区日本橋の起点付近に設定してある : 任意に変更可) *****
      var mpoint = [35.684468, 139.774606];
      var map = L.map('mapcontainer').setView(mpoint, 18);

      //3つの地図タイルを切り替える (デフォルトはオープンストリートマップ)
      var gsi =L.tileLayer('https://cyberjapandata.gsi.go.jp/xyz/std/{z}/{x}/{y}.png', 
        {attribution: "<a href='https://maps.gsi.go.jp/development/ichiran.html' target='_blank'>地理院タイル</a>"});
      var gsipale = L.tileLayer('http://cyberjapandata.gsi.go.jp/xyz/pale/{z}/{x}/{y}.png',
        {attribution: "<a href='http://portal.cyberjapan.jp/help/termsofuse.html' target='_blank'>地理院タイル</a>"});
      var osm = L.tileLayer('http://tile.openstreetmap.jp/{z}/{x}/{y}.png',
        {  attribution: "<a href='http://osm.org/copyright' target='_blank'>OpenStreetMap</a> contributors" });
      //baseMapsオブジェクトのプロパティに3つのタイルを設定
      var baseMaps = {
        "地理院地図" : gsi,
        "淡色地図" : gsipale,
        "オープンストリートマップ"  : osm
      };
      L.control.layers(baseMaps).addTo(map);
      osm.addTo(map);

      //***** 取得したルートデータ(緯度、経度)を追記していく *****
      var route = L.polyline([
           [35.684463, 139.774605],[35.684195, 139.774514],[35.683975, 139.774466],[35.683757, 139.774407],





      ], {color: "blue", weight: 5});
      //***** ルート線の描画 *****
      route.addTo(map);

      //***** (仮)クリック位置の緯度・経度取得 *****
      map.options.singleClickTimeout = 250;
      map.on('singleclick', function(e) {
      L.popup().setLatLng(e.latlng).setContent('' + e.latlng).openOn(map);
      });
    }
  </script>
</head>
<body onload="init()">
  <div id="mapcontainer" style="position:absolute;top:0;left:0;right:0;bottom:0;"></div>
</body>
</html>

4.ソースコードについて

掲載しているソースコードのライセンスは、CC0 (クレジット表示不要、改変可、商用可) とします。自由に利用して頂いてかまいません。 作品で利用しているアイコンマーカー画像ファイル等は提供していませんので、お手数ですが各自で準備して下さい。尚、ソースコードは予告なく修正を加えて 更新することがあります。予めご了承願います。また、ブラウザのソースコード表示などで表示して確認やコピー、URLから直接ダウンロードするなども自由に行ってもかまいません。 全て自己責任でお願いします。

■関連記事
・Leafletで地図を作ろう - (イントロダクション)
・Leafletで地図を作ろう - (気象庁震度観測点マップ)
・Leafletで地図を作ろう - (都道府県別人口統計マップ)
・Leafletで地図を作ろう - (富嶽三十六景浮世絵マップ)
・Leafletで地図を作ろう - (四国遍路巡礼マップ)

コメント

このブログの人気の投稿

Excelアドインで日本語形態素解析

階層構造JSONファイルの作成

HSPでコマンドプロンプトを制御する

TOP