Newer
Older
TADOKOROc1231429 / measure-path.js
// URLからパラメータを取得する関数
function getUrlParameter(name) {
    const params = new URLSearchParams(window.location.search);
    return params.get(name);
}

// チートモードかどうかを確認
const isCheatMode = getUrlParameter('cheat') !== null;

var mymap = L.map('mymap', {
    zoomControl: false,  // ズームコントロールを無効化してレンダリングを減らす
}).setView([38.891, 139.824], 16);

L.tileLayer('http://cyberjapandata.gsi.go.jp/xyz/std/{z}/{x}/{y}.png', {
    attribution: '<a href="http://maps.gsi.go.jp/development/ichiran.html">国土地理院</a>',
    updateWhenIdle: true,  // アイドル時のみタイルを更新
    updateWhenZooming: false  // ズーム中はタイルを更新しない
}).addTo(mymap);

function latlngdist(pos1, pos2) {
    return pos1.distanceTo(pos2);
}

var path = [];
var line = null;
var lineprop = {
    color: 'navy', opacity: 0.6, weight: 8
};
var sMarker, gMarker;
var totalDist = 0;
var info = document.getElementById('info');
var congratulationShown = false;

// 地点追加ボタンの参照を取得
var addPointButton = document.getElementById('addPoint');

function saveMeasurement(distance) {
    const today = new Date().toISOString().split('T')[0]; // YYYY-MM-DD形式
    let measurements = JSON.parse(localStorage.getItem('measurements')) || {};
    
    if (measurements[today]) {
        measurements[today] += distance;
    } else {
        measurements[today] = distance;
    }
    
    localStorage.setItem('measurements', JSON.stringify(measurements));
}

function displayTotalDistance() {
    const measurements = JSON.parse(localStorage.getItem('measurements')) || {};
    let totalDistance = 0;
    let history = '';
    
    for (const [date, distance] of Object.entries(measurements)) {
        totalDistance += distance;
        history += `${date}: ${Math.round(distance * 100) / 100}m\n`;
    }
    
    info.innerHTML = `総距離: ${Math.round(totalDistance * 100) / 100}m\n\n履歴:\n${history}`;
}

function updatedistance(delta) {
    totalDist += delta;
    var roundedDist = Math.round(totalDist * 100) / 100;
    if (roundedDist >= 2000 && !congratulationShown) {
        info.innerHTML = 'おつかれさま! 距離: ' + roundedDist + 'm';
        congratulationShown = true;
    } else {
        info.innerHTML = '距離: ' + roundedDist + 'm';
    }
}

function finishMeasurement() {
    // 測定を終了し、新たな測定を防ぐ
    mymap.off('click', measurePath);
    if (!isCheatMode) {
        mymap.off('locationfound', onLocationFound);
        mymap.stopLocate();
    }
    
    // 完了を視覚的に示す
    if (line) {
        line.setStyle({color: 'green', weight: 10});
    }

    saveMeasurement(totalDist);
    displayTotalDistance();
    resetPath();
}

function resetPath() {
    if (sMarker) sMarker.remove(mymap);
    if (gMarker) gMarker.remove(mymap);
    if (line) line.remove(mymap);
    line = sMarker = gMarker = null;
    
    path = [];
    totalDist = 0;
    congratulationShown = false;
    mymap.doubleClickZoom.enable();
    info.innerHTML = 'クリアしました';

    // イベントリスナーを再設定
    if (isCheatMode) {
        mymap.on('click', measurePath);
    } else {
        mymap.on('locationfound', onLocationFound);
        mymap.locate({watch: true, enableHighAccuracy: true});
    }
}

function throttle(func, limit) {
    let lastFunc;
    let lastRan;
    return function() {
        const context = this;
        const args = arguments;
        if (!lastRan) {
            func.apply(context, args);
            lastRan = Date.now();
        } else {
            clearTimeout(lastFunc);
            lastFunc = setTimeout(function() {
                if ((Date.now() - lastRan) >= limit) {
                    func.apply(context, args);
                    lastRan = Date.now();
                }
            }, limit - (Date.now() - lastRan));
        }
    }
}

function addPointToPath(latlng) {
    var imsg = '北緯 ' + latlng.lat + ' 東経 ' + latlng.lng;
    mymap.doubleClickZoom.disable();
    
    if (path.length > 1 && latlng.equals(path[path.length-1])) {
        return null;
    }
    
    path.push(latlng);
    
    if (path.length == 1) {
        sMarker = L.marker(path[0]).addTo(mymap).bindPopup(imsg);
        gMarker = L.marker(path[0]).addTo(mymap).bindPopup(imsg);
        line = L.polyline(path, lineprop).addTo(mymap);
        info.innerHTML = imsg;
    } else {
        line.addLatLng(latlng);
        gMarker.setLatLng(latlng);
    }
    
    if (path.length > 1) {
        updatedistance(latlngdist(path[path.length-2], path[path.length-1]));
    }
}

var measurePath = throttle(function(e) {
    var latlng = e.latlng;
    addPointToPath(latlng);
    
    if (e.originalEvent && e.originalEvent.shiftKey) {
        resetPath(e);
    }
}, 300);

function removePoint(e) {
    if (path.length > 1) {
        let rm = path.pop();
        updatedistance(-latlngdist(rm, path[path.length-1]));
        line.setLatLngs(path);
        gMarker.setLatLng(path[path.length-1]);
        mymap.panTo(path[path.length-1]);
    } else {
        info.innerHTML = '始点は削れません。'
    }
}

// チートモードの確認と地点追加ボタンの表示/非表示の設定
if (isCheatMode) {
    mymap.on('click', measurePath);
    addPointButton.style.display = 'none';
} else {
    addPointButton.style.display = 'inline-block';
    // 地点追加ボタンのイベントリスナー
    addPointButton.addEventListener('click', function() {
        mymap.locate({setView: true, maxZoom: 16});
    });
    
    // 位置情報取得成功時の処理
    function onLocationFound(e) {
        addPointToPath(e.latlng);
    }

    mymap.on('locationfound', onLocationFound);

    // 位置情報取得失敗時の処理
    mymap.on('locationerror', function(e) {
        alert("位置情報を取得できませんでした: " + e.message);
    });

    // 初期位置取得
    mymap.locate({setView: true, maxZoom: 16});
}

mymap.on('contextmenu', removePoint);

document.getElementById('undo').addEventListener('click', removePoint);
document.getElementById('finish').addEventListener('click', finishMeasurement);

// 初期表示時に総距離を表示
displayTotalDistance();