JavaScript+Python(Django)で京都市バスの路線図を描画する処理

これまでのいきさつ

京都市バスのオープンデータを使ってバス路線図を描いた。


現在以下のリンクにて公開中。
https://replica-of-kyotobusmap.herokuapp.com/main/


使用したライブラリ

jQuery
https://jquery.com/


Leaflet JavaScriptで地図を扱うライブラリ
https://leafletjs.com/


Django PythonのWebフレームワーク
https://www.djangoproject.com/


JavaScript側の処理(リクエスト送信)

f:id:tatsuzaki98:20190704171129p:plain

バス停(画像の緑色の円)をクリックすると、下の関数を呼び出す。
すでに表示されてる線(pline)があれば一度全て削除。
その後、サーバーに非同期通信でクリックした緯度と経度を送信する。
CSRFトークンに関しては前記事で。

function circleClick(event) {
    while(plines.length > 0){
        map.removeLayer(plines.pop());
    }

    let lat = event.latlng.lat;
    let lng = event.latlng.lng;
    let csrftoken = document.cookie.substring('csrftoken='.length);
    $.ajax({
        url: '/main/get_stop_info/',
        data: {'lat': lat, 'lng': lng},
        beforeSend: function (xhr) {
            xhr.setRequestHeader("X-CSRFToken", csrftoken);
        },
        type: "POST",
        dataType: 'json',
        success: getStopInfo,
    });
}

Django側の処理(レスポンス送信)

受け取った経度と緯度(lng, lat)から、該当するバス停を検索する。
バス停を通る路線を、順番通りに並び替えてリスト化。
JSON形式でJavaScript側に送り返す。

ここはがんばればもう少し簡潔に書けそう。

def get_stop_info(request):
    lat, lng = float(request.POST['lat']), float(request.POST['lng'])

    stop = 0
    for s in Stop.objects.all():
        if abs(s.lat - lat) < 0.0007 and abs(s.lng - lng) < 0.0007:
            stop = s
            break

    if stop == 0:
        return JsonResponse({'StopNotFound': 1})

    route_set = []
    for line in stop.line_set.all():
        route = []
        for stop_pk in line.route.split(','):
            s = Stop.objects.get(pk=int(stop_pk)+1)
            route.append([s.lat, s.lng])
        route_set.append([route, line.line_name])

    line_name_count = Counter([route[1] for route in route_set])

    response = {
        'stop_name': stop.stop_name,
        'route_set': route_set,
        'line_name_set': sorted(list(line_name_count.keys())),
    }
    return JsonResponse(response)

JavaScript側の処理(路線描画)

レスポンスが無事に返ってきたら以下の関数が実行される。
route_setのデータに沿って線を描画する。
基本的にはLeafletの説明書通り。

function getStopInfo(response){
    for(let route of response['route_set']) {
        let pline = L.polyline(route[0], {weight: 5, color:'#FF0000'}).addTo(map);
        pline.on('click', function(e){clickLine(e, route)});
        plines.push(pline);
    }
    $('h3').text(response['stop_name']);
}

function clickLine(event, route){
    while(plines.length > 0){
        map.removeLayer(plines.pop());
    }
    let pline = L.polyline(route[0], {weight: 5, color:'#FF0000'}).addTo(map);
    $('h3').text(route[1]);
    plines.push(pline);
}

f:id:tatsuzaki98:20190704172857p:plain

無事に表示されている。


今後のこと

時刻表の追加。これは近いうちに。

路線を描画する動作が重いので、重複する線をあらかじめデータベース側で省略するなどして、最適化する。

見た目も今のままだた少し無骨すぎるのでちょくちょく直していきたい。