286 lines
		
	
	
		
			No EOL
		
	
	
		
			10 KiB
		
	
	
	
		
			HTML
		
	
	
	
	
	
			
		
		
	
	
			286 lines
		
	
	
		
			No EOL
		
	
	
		
			10 KiB
		
	
	
	
		
			HTML
		
	
	
	
	
	
<!DOCTYPE html>
 | 
						|
<html lang="en">
 | 
						|
<head>
 | 
						|
    <meta charset="UTF-8">
 | 
						|
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
 | 
						|
    <title>logbook2map</title>
 | 
						|
 | 
						|
    <link rel="stylesheet" href="https://unpkg.com/maplibre-gl/dist/maplibre-gl.css">
 | 
						|
    <style>
 | 
						|
        body, html {
 | 
						|
            margin: 0;
 | 
						|
            padding: 0;
 | 
						|
            width: 100vw;
 | 
						|
            height: 100vh;
 | 
						|
            overflow: hidden;
 | 
						|
        }
 | 
						|
 | 
						|
        #map {
 | 
						|
            width: 100%;
 | 
						|
            height: 100%;
 | 
						|
        }
 | 
						|
 | 
						|
        #input_modal {
 | 
						|
            position: absolute;
 | 
						|
            top: 0;
 | 
						|
            left: 0;
 | 
						|
            width: 100%;
 | 
						|
            height: 100%;
 | 
						|
            background-color: rgba(0, 0, 0, 0.5);
 | 
						|
            display: flex;
 | 
						|
            justify-content: center;
 | 
						|
            align-items: center;
 | 
						|
            
 | 
						|
            display: none;
 | 
						|
 | 
						|
            textarea {
 | 
						|
                width: 80%;
 | 
						|
                height: 80%;
 | 
						|
                font-size: 1.5em;
 | 
						|
                min-width: 20em;
 | 
						|
                min-height: 20em;
 | 
						|
                max-width: 40em;
 | 
						|
                max-height: 40em;
 | 
						|
            }
 | 
						|
        }
 | 
						|
    </style>
 | 
						|
</head>
 | 
						|
<body>
 | 
						|
    <div id="map"></div>
 | 
						|
 | 
						|
    <div id="input_modal">
 | 
						|
        <textarea placeholder="ICAO_departure;ICAO_destination


Validate with Ctrl+Enter
Load new data with Ctrl+O"></textarea>
 | 
						|
    </div>
 | 
						|
 | 
						|
    <script src="https://unpkg.com/maplibre-gl/dist/maplibre-gl.js"></script>
 | 
						|
    <script>
 | 
						|
        const osmStyle = {
 | 
						|
            "version": 8,
 | 
						|
                "sources": {
 | 
						|
                "osm": {
 | 
						|
                        "type": "raster",
 | 
						|
                        "tiles": ["https://a.tile.openstreetmap.org/{z}/{x}/{y}.png"],
 | 
						|
                        "tileSize": 256,
 | 
						|
                "attribution": "© OpenStreetMap Contributors",
 | 
						|
                "maxzoom": 19
 | 
						|
                }
 | 
						|
            },
 | 
						|
            "layers": [
 | 
						|
                {
 | 
						|
                "id": "osm",
 | 
						|
                "type": "raster",
 | 
						|
                "source": "osm" // This must match the source key above
 | 
						|
                }
 | 
						|
            ]
 | 
						|
        };
 | 
						|
        var map = new maplibregl.Map({
 | 
						|
            container: 'map', // container id
 | 
						|
            style: osmStyle, // style URL
 | 
						|
            center: [17.97, 57.91], // starting position [lng, lat]
 | 
						|
            zoom: 3.7 // starting zoom
 | 
						|
        });
 | 
						|
 | 
						|
        function _updateMap(csvData, airportsData) {
 | 
						|
            const lines = csvData.split('\n');
 | 
						|
 | 
						|
            const features_nav = new Array();
 | 
						|
            const features_local = new Array();
 | 
						|
            const airport_name_to_ident = {
 | 
						|
                "Norberg": "SE-0004"
 | 
						|
            };
 | 
						|
            for(const line of lines) {
 | 
						|
                const [from, to] = line.split(',');
 | 
						|
 | 
						|
                let fromAirport = airportsData[from];
 | 
						|
                let toAirport = airportsData[to];
 | 
						|
 | 
						|
                if(!fromAirport) {
 | 
						|
                    fromAirport = airportsData[airport_name_to_ident[from]];
 | 
						|
                    if(!fromAirport) { alert(`Airport ${from} not found`); return; }
 | 
						|
                }
 | 
						|
                if(!toAirport) {
 | 
						|
                    toAirport = airportsData[airport_name_to_ident[to]];
 | 
						|
                    if(!toAirport) { alert(`Airport ${to} not found`); return; }
 | 
						|
                }
 | 
						|
 | 
						|
                const fromLongitude = parseFloat(fromAirport.longitude);
 | 
						|
                const fromLatitude = parseFloat(fromAirport.latitude);
 | 
						|
                const toLongitude = parseFloat(toAirport.longitude);
 | 
						|
                const toLatitude = parseFloat(toAirport.latitude);
 | 
						|
 | 
						|
                // if from == to, draw a point
 | 
						|
                if(fromAirport == toAirport) {
 | 
						|
                    features_local.push( {
 | 
						|
                        type: 'Feature',
 | 
						|
                        properties: {
 | 
						|
                            from,
 | 
						|
                            to
 | 
						|
                        },
 | 
						|
                        geometry: {
 | 
						|
                            type: 'Point',
 | 
						|
                            coordinates: [fromLongitude, fromLatitude]
 | 
						|
                        }
 | 
						|
                    } );
 | 
						|
                }
 | 
						|
                else {
 | 
						|
                    features_nav.push( {
 | 
						|
                        type: 'Feature',
 | 
						|
                        properties: {
 | 
						|
                            from,
 | 
						|
                            to
 | 
						|
                        },
 | 
						|
                        geometry: {
 | 
						|
                            type: 'LineString',
 | 
						|
                            coordinates: [
 | 
						|
                                [fromLongitude, fromLatitude],
 | 
						|
                                [toLongitude, toLatitude]
 | 
						|
                            ]
 | 
						|
                        }
 | 
						|
                    } );
 | 
						|
 | 
						|
                    // Add a point for each airport
 | 
						|
                    features_nav.push( {
 | 
						|
                        type: 'Feature',
 | 
						|
                        properties: {
 | 
						|
                            name: fromAirport.name
 | 
						|
                        },
 | 
						|
                        geometry: {
 | 
						|
                            type: 'Point',
 | 
						|
                            coordinates: [fromLongitude, fromLatitude]
 | 
						|
                        }
 | 
						|
                    } );
 | 
						|
                    
 | 
						|
                    features_nav.push( {
 | 
						|
                        type: 'Feature',
 | 
						|
                        properties: {
 | 
						|
                            name: toAirport.name
 | 
						|
                        },
 | 
						|
                        geometry: {
 | 
						|
                            type: 'Point',
 | 
						|
                            coordinates: [toLongitude, toLatitude]
 | 
						|
                        }
 | 
						|
                    } );
 | 
						|
                }
 | 
						|
            }
 | 
						|
 | 
						|
            map.addSource('nav', {
 | 
						|
                type: 'geojson',
 | 
						|
                data: {
 | 
						|
                    type: 'FeatureCollection',
 | 
						|
                    features: features_nav
 | 
						|
                }
 | 
						|
            });
 | 
						|
 | 
						|
            map.addSource('local', {
 | 
						|
                type: 'geojson',
 | 
						|
                data: {
 | 
						|
                    type: 'FeatureCollection',
 | 
						|
                    features: features_local
 | 
						|
                }
 | 
						|
            });
 | 
						|
 | 
						|
            map.addLayer({
 | 
						|
                id: 'nav_lines',
 | 
						|
                type: 'line',
 | 
						|
                source: 'nav',
 | 
						|
                layout: {
 | 
						|
                    'line-join': 'round',
 | 
						|
                    'line-cap': 'round'
 | 
						|
                },
 | 
						|
                paint: {
 | 
						|
                    'line-color': '#ce42f5',
 | 
						|
                    'line-width': 2
 | 
						|
                }
 | 
						|
            });
 | 
						|
 | 
						|
            map.addLayer({
 | 
						|
                id: 'nav_points',
 | 
						|
                type: 'circle',
 | 
						|
                source: 'nav',
 | 
						|
                paint: {
 | 
						|
                    'circle-radius': 5,
 | 
						|
                    'circle-color': '#ce42f5'
 | 
						|
                },
 | 
						|
                filter: ['==', '$type', 'Point']
 | 
						|
            });
 | 
						|
 | 
						|
            map.addLayer({
 | 
						|
                id: 'local_points',
 | 
						|
                type: 'circle',
 | 
						|
                source: 'local',
 | 
						|
                paint: {
 | 
						|
                    'circle-radius': 5,
 | 
						|
                    'circle-color': '#f00'
 | 
						|
                },
 | 
						|
                filter: ['==', '$type', 'Point']
 | 
						|
            });
 | 
						|
        }
 | 
						|
 | 
						|
        function updateMap(csvData) {
 | 
						|
            // download https://davidmegginson.github.io/ourairports-data/airports.csv if not locally available
 | 
						|
            const request = indexedDB.open('my_database', 1);
 | 
						|
            request.onupgradeneeded = function(event) {
 | 
						|
                const db = event.target.result;
 | 
						|
                const objectStore = db.createObjectStore('airports');
 | 
						|
            };
 | 
						|
            request.onerror = function(event) { alert('Error while opening database:\n' + event.target.error); };
 | 
						|
            request.onsuccess = function(event) {
 | 
						|
                const db = event.target.result;
 | 
						|
                const transaction = db.transaction(['airports'], 'readwrite');
 | 
						|
                const objectStore = transaction.objectStore('airports');
 | 
						|
 | 
						|
                const getRequest = objectStore.get('airports');
 | 
						|
                getRequest.onerror = function(event) { alert("Error getting data:\n" + event.target.error); };
 | 
						|
                getRequest.onsuccess = function(event) {
 | 
						|
                    if (!event.target.result) {
 | 
						|
                        // Data not found, fetch and store it
 | 
						|
                        fetch('https://davidmegginson.github.io/ourairports-data/airports.csv')
 | 
						|
                            .then(response => response.text())
 | 
						|
                            .then(data => {
 | 
						|
                                console.info('Downloaded airports.csv, parsing the data...');
 | 
						|
                                const airports = {};
 | 
						|
                                for(const airport of data.split('\n')) {
 | 
						|
                                    const [_id, ident, _type, name, latitude, longitude] = airport.split(',');
 | 
						|
                                    if(!ident) continue;
 | 
						|
                                    airports[ident.replace(/^"|"$/g, '')] = { name, latitude, longitude };
 | 
						|
                                }
 | 
						|
 | 
						|
                                const transaction = db.transaction(['airports'], 'readwrite');
 | 
						|
                                const objectStore = transaction.objectStore('airports');
 | 
						|
                                objectStore.put(airports, 'airports');
 | 
						|
                                _updateMap(csvData, airports);
 | 
						|
                            })
 | 
						|
                            .catch(error => { alert('Error while downloading airports.csv: \n' + error); });
 | 
						|
                    } else {
 | 
						|
                        console.log('Data found in local storage!');
 | 
						|
                        _updateMap(csvData, event.target.result);
 | 
						|
                    }
 | 
						|
                };
 | 
						|
            };
 | 
						|
        }
 | 
						|
 | 
						|
        function showInputModal() {
 | 
						|
            document.getElementById('input_modal').style.display = 'flex';
 | 
						|
 | 
						|
            const textarea = document.querySelector('#input_modal textarea');
 | 
						|
            textarea.focus();
 | 
						|
            textarea.addEventListener('keydown', event => {
 | 
						|
                if(event.ctrlKey && event.key === 'Enter') {
 | 
						|
                    const csvData = textarea.value;
 | 
						|
                    updateMap(csvData);
 | 
						|
                    document.getElementById('input_modal').style.display = 'none';
 | 
						|
                }
 | 
						|
            });
 | 
						|
        }
 | 
						|
 | 
						|
        showInputModal();
 | 
						|
 | 
						|
        document.addEventListener('keydown', event => {
 | 
						|
            if(event.ctrlKey && event.key === 'o') {
 | 
						|
                event.preventDefault();
 | 
						|
                showInputModal();
 | 
						|
            }
 | 
						|
        });
 | 
						|
    </script>
 | 
						|
</body>
 | 
						|
</html> |