← back to Sublease Agentabrams

scripts/geocode-backfill.js

28 lines

'use strict';
// Backfill lat/lng for listings missing coords, using free OSM Nominatim (city-level ok).
// Polite: 1 req/sec, descriptive UA. $0.
const { pool, httpGet, sleep } = require('../crawl/base-crawler');

async function nominatim(qstr) {
  const url = 'https://nominatim.openstreetmap.org/search?format=json&limit=1&q=' + encodeURIComponent(qstr);
  const r = await httpGet(url, { timeout: 15000, headers: { 'User-Agent': 'SubleaseMarketplace/0.1 (agentabrams.com)' } });
  if (!r.ok) return null;
  try { const j = JSON.parse(r.text); return j[0] ? { lat: +j[0].lat, lng: +j[0].lon } : null; } catch { return null; }
}

(async () => {
  const { rows } = await pool.query(
    `SELECT id, address, city, state FROM listings WHERE (lat IS NULL OR lng IS NULL) AND (city IS NOT NULL OR address IS NOT NULL) ORDER BY id`);
  console.log(`${rows.length} listings need coords`);
  let done = 0;
  for (const r of rows) {
    const qstr = [r.address, r.city, r.state || 'CA', 'USA'].filter(Boolean).join(', ');
    const g = await nominatim(qstr);
    if (g) { await pool.query('UPDATE listings SET lat=$2, lng=$3 WHERE id=$1', [r.id, g.lat, g.lng]); done++; process.stdout.write('.'); }
    else process.stdout.write('x');
    await sleep(1100); // Nominatim policy
  }
  console.log(`\ngeocoded ${done}/${rows.length}`);
  await pool.end();
})();