Last active
July 28, 2025 10:52
-
-
Save jonalling/9f1a52ebb0ef77554a193162e467914c to your computer and use it in GitHub Desktop.
HTML page for converting Google Maps exports into Apple Maps links
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| <!-- Created by Human Crafted (humancrafted.co) with help from GPT-o3-mini-high --> | |
| <!-- This HTML file allows users to upload a CSV or GeoJSON file containing saved Google Maps places and view them in Apple Maps. --> | |
| <!-- To use, save HTML file to a folder on your computer, open in a browser. Select your exported places file. --> | |
| <!DOCTYPE html> | |
| <html> | |
| <head> | |
| <meta charset="UTF-8"> | |
| <title>Saved Places</title> | |
| <style> | |
| body { font-family: sans-serif; margin: 2em; } | |
| .place { padding: 1em; border-bottom: 1px solid #ccc; } | |
| .place a { color: blue; text-decoration: none; } | |
| .place a.visited { color: lightgrey; } | |
| .place a:hover { text-decoration: underline; } | |
| #fileInput { margin-bottom: 1em; } | |
| </style> | |
| </head> | |
| <body> | |
| <h1>Saved Places</h1> | |
| <input type="file" id="fileInput" accept=".json,.csv"> | |
| <div id="places"></div> | |
| <script> | |
| const fileInput = document.getElementById('fileInput'); | |
| const placesContainer = document.getElementById('places'); | |
| fileInput.addEventListener('change', function(event) { | |
| const file = event.target.files[0]; | |
| if (!file) return; | |
| const reader = new FileReader(); | |
| reader.onload = function(e) { | |
| const content = e.target.result; | |
| // Check the file extension to determine format | |
| if (file.name.toLowerCase().endsWith('.csv')) { | |
| parseCSV(content); | |
| } else { | |
| try { | |
| const geojson = JSON.parse(content); | |
| parseGeoJSON(geojson); | |
| } catch (error) { | |
| placesContainer.innerHTML = '<p>Error reading JSON file. Please ensure it is valid GeoJSON.</p>'; | |
| console.error("Error parsing JSON:", error); | |
| } | |
| } | |
| }; | |
| reader.readAsText(file); | |
| }); | |
| // Function to parse GeoJSON files | |
| function parseGeoJSON(geojson) { | |
| placesContainer.innerHTML = ''; // Clear any existing content | |
| if (!geojson.features || !Array.isArray(geojson.features)) { | |
| placesContainer.innerHTML = '<p>Invalid GeoJSON format.</p>'; | |
| return; | |
| } | |
| geojson.features.forEach(feature => { | |
| // Check that the feature has a properties.location object with address and name | |
| if (!feature.properties || !feature.properties.location || | |
| !feature.properties.location.address || !feature.properties.location.name) { | |
| // Skip this feature if location info is missing | |
| return; | |
| } | |
| const { name, address } = feature.properties.location; | |
| // GeoJSON stores coordinates as [longitude, latitude] | |
| const [lon, lat] = feature.geometry.coordinates; | |
| // Create a unique key for this location using name and address | |
| const key = `visited_${encodeURIComponent(name + address)}`; | |
| // Construct the Apple Maps URL using address and coordinates | |
| const appleUrl = `http://maps.apple.com/?address=${encodeURIComponent(address)}&ll=${lat},${lon}`; | |
| // Create the display container | |
| const div = document.createElement('div'); | |
| div.className = 'place'; | |
| div.innerHTML = `<strong>${name}</strong><br>${address}<br>`; | |
| // Create the clickable link element | |
| const link = document.createElement('a'); | |
| link.href = appleUrl; | |
| link.target = "_blank"; | |
| link.textContent = "Open in Apple Maps"; | |
| // Mark as visited if already clicked | |
| if (localStorage.getItem(key)) { | |
| link.classList.add('visited'); | |
| } | |
| // Save visited state on click | |
| link.addEventListener('click', () => { | |
| localStorage.setItem(key, "true"); | |
| link.classList.add('visited'); | |
| }); | |
| div.appendChild(link); | |
| placesContainer.appendChild(div); | |
| }); | |
| } | |
| // Function to parse CSV files (assumes header: Title,Note,URL,Comment) | |
| function parseCSV(content) { | |
| placesContainer.innerHTML = ''; // Clear existing content | |
| // Split by newline and filter out any empty lines | |
| const lines = content.split('\n').filter(line => line.trim() !== ''); | |
| if (lines.length < 2) { | |
| placesContainer.innerHTML = '<p>No data found in CSV.</p>'; | |
| return; | |
| } | |
| // Parse header to determine indexes (in case order changes) | |
| const header = lines[0].split(',').map(h => h.trim()); | |
| const titleIndex = header.indexOf('Title'); | |
| const urlIndex = header.indexOf('URL'); | |
| if (titleIndex === -1 || urlIndex === -1) { | |
| placesContainer.innerHTML = '<p>CSV file does not contain the required columns.</p>'; | |
| return; | |
| } | |
| // Process each CSV row | |
| for (let i = 1; i < lines.length; i++) { | |
| // Split row by commas. (This simple approach assumes no commas in fields.) | |
| const row = lines[i].split(',').map(cell => cell.trim()); | |
| // If row is incomplete, skip it. | |
| if (row.length < header.length) continue; | |
| const title = row[titleIndex]; | |
| const googleUrl = row[urlIndex]; | |
| // For CSV, we'll generate an Apple Maps URL using just the title as a search query. | |
| const appleUrl = `http://maps.apple.com/?q=${encodeURIComponent(title)}`; | |
| // Create a unique key using title and googleUrl | |
| const key = `visited_${encodeURIComponent(title + googleUrl)}`; | |
| // Build the display for the CSV entry | |
| const div = document.createElement('div'); | |
| div.className = 'place'; | |
| div.innerHTML = `<strong>${title}</strong><br>`; | |
| const link = document.createElement('a'); | |
| link.href = appleUrl; | |
| link.target = "_blank"; | |
| link.textContent = "Open in Apple Maps"; | |
| if (localStorage.getItem(key)) { | |
| link.classList.add('visited'); | |
| } | |
| link.addEventListener('click', () => { | |
| localStorage.setItem(key, "true"); | |
| link.classList.add('visited'); | |
| }); | |
| div.appendChild(link); | |
| placesContainer.appendChild(div); | |
| } | |
| } | |
| </script> | |
| </body> | |
| </html> |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment