personal-hub/tools/amap_distance_checker.js
StillHammer 3c8162c990 Sync couple_matters: December crisis, separation agreement, daily check v2, xiaozhu search
Major updates:
- December 2025 crisis documentation and separation agreement
- Daily check system v2 with multiple card categories
- Xiaozhu rental search tools and results
- Exit plan documentation
- Message drafts for family communication
- Confluent moved to CONSTANT
- Updated profiles and promises

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
2025-12-23 07:04:02 +08:00

325 lines
9.5 KiB
JavaScript

const https = require('https');
const fs = require('fs');
/**
* Amap Distance Checker - Calculate transit time to Jiaotong University
* Uses Amap (高德地图) Web Service API
*/
const CONFIG = {
// Target: Jiaotong University Xujiahui Campus
targetName: '上海交通大学徐汇校区',
targetCoords: '121.4367,31.1880', // lon,lat format for Amap
// Amap API (free tier, no key needed for basic geocoding)
// For route planning, we'll use web scraping approach
// Input/Output
inputFile: './xiaozhu_raw_listings.json',
outputFile: './xiaozhu_with_distances.json',
outputMarkdown: './xiaozhu_with_distances.md'
};
console.log('🗺️ Amap Distance Checker');
console.log(`🎯 Target: ${CONFIG.targetName}`);
console.log(`📍 Coordinates: ${CONFIG.targetCoords}\n`);
// Helper to make HTTPS requests
function httpsGet(url) {
return new Promise((resolve, reject) => {
https.get(url, (res) => {
let data = '';
res.on('data', chunk => data += chunk);
res.on('end', () => {
try {
resolve(JSON.parse(data));
} catch (e) {
resolve(data);
}
});
}).on('error', reject);
});
}
// Extract location from title
function extractLocation(title) {
// Common patterns in Xiaozhu titles
const patterns = [
/近(.{2,10})/, // 近X
/(.{2,10})地铁/, // X地铁
/(.{2,10})医院/, // X医院
/(.{2,10})路/, // X路
];
const locations = [];
for (const pattern of patterns) {
const matches = title.matchAll(new RegExp(pattern, 'g'));
for (const match of matches) {
if (match[1] && match[1].length >= 2) {
locations.push(match[1]);
}
}
}
// Also extract subway lines
const metroPattern = /(\d+)号线/g;
const metroMatches = [...title.matchAll(metroPattern)];
const metroLines = metroMatches.map(m => `Line ${m[1]}`);
return {
landmarks: [...new Set(locations)].slice(0, 3),
metroLines: [...new Set(metroLines)]
};
}
// Estimate distance category based on landmarks and metro lines
function estimateDistance(listing) {
const title = listing.title || '';
const location = listing.location || '';
const fullText = (title + ' ' + location).toLowerCase();
// Extract location info
const locationInfo = extractLocation(title);
// Keywords indicating proximity to Jiaotong University
const veryClose = [
'交通大学', '徐家汇', '上海交大', 'jiaotong', 'xujiahui'
];
const close = [
'衡山路', 'hengshan', '淮海路', 'huaihai',
'常熟路', 'changshu', '肇嘉浜路'
];
const medium = [
'上海体育馆', 'stadium', '龙华', 'longhua',
'东安路', 'dongan', '漕河泾'
];
const far = [
'南站', 'south station', '华东理工', '闵行', 'minhang'
];
// Check keywords
for (const keyword of veryClose) {
if (fullText.includes(keyword)) {
return {
category: 'very_close',
estimatedMinutes: 10,
confidence: 'high',
reason: `Mentions ${keyword}`,
locationInfo
};
}
}
for (const keyword of close) {
if (fullText.includes(keyword)) {
return {
category: 'close',
estimatedMinutes: 15,
confidence: 'medium',
reason: `Near ${keyword}`,
locationInfo
};
}
}
for (const keyword of medium) {
if (fullText.includes(keyword)) {
return {
category: 'medium',
estimatedMinutes: 25,
confidence: 'medium',
reason: `Around ${keyword}`,
locationInfo
};
}
}
for (const keyword of far) {
if (fullText.includes(keyword)) {
return {
category: 'far',
estimatedMinutes: 40,
confidence: 'medium',
reason: `Far area - ${keyword}`,
locationInfo
};
}
}
// Check metro lines (10, 11 are best for Jiaotong Uni)
if (title.includes('10号线') || title.includes('11号线')) {
return {
category: 'close',
estimatedMinutes: 15,
confidence: 'high',
reason: 'On Line 10 or 11 (direct to campus)',
locationInfo
};
}
// Lines with transfer
if (title.includes('1号线') || title.includes('9号线')) {
return {
category: 'medium',
estimatedMinutes: 25,
confidence: 'medium',
reason: 'Requires 1 transfer to Line 10/11',
locationInfo
};
}
return {
category: 'unknown',
estimatedMinutes: 30,
confidence: 'low',
reason: 'Unable to determine location',
locationInfo
};
}
async function processListings() {
// Load listings
console.log(`📂 Loading listings from ${CONFIG.inputFile}...`);
let listings;
try {
const data = fs.readFileSync(CONFIG.inputFile, 'utf8');
listings = JSON.parse(data);
} catch (err) {
console.error(`❌ Error loading file: ${err.message}`);
console.log('\n💡 Make sure you ran xiaozhu_fixed.js first to generate the listings.');
return;
}
console.log(`✅ Loaded ${listings.length} listings\n`);
// Process each listing
console.log('🔍 Analyzing distances...\n');
const processed = listings.map((listing, i) => {
const distance = estimateDistance(listing);
console.log(`${i + 1}. ${listing.title?.substring(0, 60) || 'Untitled'}`);
console.log(` 💰 ¥${listing.priceDaily}/day`);
console.log(` 📍 Distance: ${distance.category.toUpperCase()} (~${distance.estimatedMinutes} min)`);
console.log(` 🔍 Reason: ${distance.reason}`);
if (distance.locationInfo.landmarks.length > 0) {
console.log(` 🏷️ Landmarks: ${distance.locationInfo.landmarks.join(', ')}`);
}
if (distance.locationInfo.metroLines.length > 0) {
console.log(` 🚇 Metro: ${distance.locationInfo.metroLines.join(', ')}`);
}
console.log('');
return {
...listing,
distance: {
...distance,
toJiaotongUniversity: distance.estimatedMinutes
}
};
});
// Sort by distance (closest first)
const sorted = [...processed].sort((a, b) => {
// Prioritize by distance, then by price
if (a.distance.toJiaotongUniversity !== b.distance.toJiaotongUniversity) {
return a.distance.toJiaotongUniversity - b.distance.toJiaotongUniversity;
}
return (a.priceDaily || 9999) - (b.priceDaily || 9999);
});
// Save results
fs.writeFileSync(CONFIG.outputFile, JSON.stringify(sorted, null, 2));
console.log(`💾 Saved results to ${CONFIG.outputFile}\n`);
// Generate markdown
generateMarkdown(sorted);
// Print summary
printSummary(sorted);
}
function generateMarkdown(listings) {
let md = '# Xiaozhu Listings - With Distance to Jiaotong University\n\n';
md += `**Target:** ${CONFIG.targetName}\n`;
md += `**Sorted by:** Distance (closest first)\n\n`;
md += '| # | Title | Price/Day | Total (29d) | Distance | Est. Time | Kitchen | Metro Lines |\n';
md += '|---|-------|-----------|-------------|----------|-----------|---------|-------------|\n';
listings.forEach((l, i) => {
const total = (l.priceDaily || 0) * 29;
const distanceEmoji =
l.distance.category === 'very_close' ? '🟢' :
l.distance.category === 'close' ? '🟡' :
l.distance.category === 'medium' ? '🟠' :
l.distance.category === 'far' ? '🔴' : '⚪';
md += `| ${i + 1} `;
md += `| ${(l.title || 'Untitled').substring(0, 50)}... `;
md += `| ¥${l.priceDaily || '-'} `;
md += `| ¥${total} `;
md += `| ${distanceEmoji} ${l.distance.category} `;
md += `| ~${l.distance.toJiaotongUniversity} min `;
md += `| ${l.hasKitchen ? '✓' : '✗'} `;
md += `| ${l.distance.locationInfo.metroLines.join(', ') || '-'} |\n`;
});
md += '\n## Distance Categories\n\n';
md += '- 🟢 **VERY CLOSE**: < 15 min - Direct area (徐家汇, 交通大学)\n';
md += '- 🟡 **CLOSE**: 15-20 min - Nearby (衡山路, Line 10/11)\n';
md += '- 🟠 **MEDIUM**: 20-30 min - Requires transfer (Line 1/9)\n';
md += '- 🔴 **FAR**: 30+ min - Far areas (南站, 闵行)\n';
md += '- ⚪ **UNKNOWN**: Distance unclear\n\n';
md += '## Best Options (Closest + Kitchen + Budget)\n\n';
const best = listings
.filter(l => l.hasKitchen)
.filter(l => (l.priceDaily * 29) <= 5800) // Allow slight budget flex
.slice(0, 5);
best.forEach((l, i) => {
md += `### ${i + 1}. ${l.title}\n\n`;
md += `- **Price:** ¥${l.priceDaily}/day (¥${l.priceDaily * 29} total)\n`;
md += `- **Distance:** ${l.distance.category} (~${l.distance.toJiaotongUniversity} min)\n`;
md += `- **Reason:** ${l.distance.reason}\n`;
md += `- **Kitchen:** ${l.hasKitchen ? 'Yes ✓' : 'No'}\n`;
md += `- **Metro:** ${l.distance.locationInfo.metroLines.join(', ') || 'Not specified'}\n\n`;
});
fs.writeFileSync(CONFIG.outputMarkdown, md);
console.log(`📝 Saved markdown to ${CONFIG.outputMarkdown}\n`);
}
function printSummary(listings) {
console.log('=' .repeat(60));
console.log('📊 SUMMARY - TOP 5 CLOSEST WITH KITCHEN\n');
const topClosest = listings
.filter(l => l.hasKitchen)
.slice(0, 5);
topClosest.forEach((l, i) => {
const total = l.priceDaily * 29;
const inBudget = total <= 5000 ? '✅' : '⚠️';
console.log(`${i + 1}. ${l.title?.substring(0, 60)}`);
console.log(` 💰 ¥${l.priceDaily}/day = ¥${total} total ${inBudget}`);
console.log(` 📍 ${l.distance.category.toUpperCase()} - ~${l.distance.toJiaotongUniversity} min`);
console.log(` 🔍 ${l.distance.reason}`);
console.log('');
});
console.log('=' .repeat(60));
console.log('\n✅ Done! Check xiaozhu_with_distances.md for full results.');
}
// Run
processListings().catch(console.error);