Skip to content

Instantly share code, notes, and snippets.

@jonalling
Last active July 28, 2025 10:52
Show Gist options
  • Save jonalling/9f1a52ebb0ef77554a193162e467914c to your computer and use it in GitHub Desktop.
Save jonalling/9f1a52ebb0ef77554a193162e467914c to your computer and use it in GitHub Desktop.
HTML page for converting Google Maps exports into Apple Maps links
<!-- 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