Got the automated address grabber working
This commit is contained in:
parent
e1ddebc90a
commit
5e2a6320ff
@ -24,6 +24,8 @@ COPY --from=builder /app/node_modules ./node_modules
|
|||||||
COPY package*.json ./
|
COPY package*.json ./
|
||||||
COPY server.js ./
|
COPY server.js ./
|
||||||
COPY public ./public
|
COPY public ./public
|
||||||
|
COPY routes ./routes
|
||||||
|
COPY services ./services
|
||||||
|
|
||||||
# Create non-root user
|
# Create non-root user
|
||||||
RUN addgroup -g 1001 -S nodejs && \
|
RUN addgroup -g 1001 -S nodejs && \
|
||||||
|
|||||||
@ -579,8 +579,11 @@ function showAddLocationModal(lat, lng) {
|
|||||||
addressInput.value = result.formattedAddress || result.fullAddress;
|
addressInput.value = result.formattedAddress || result.fullAddress;
|
||||||
} else {
|
} else {
|
||||||
addressInput.value = ''; // Clear if lookup fails
|
addressInput.value = ''; // Clear if lookup fails
|
||||||
showStatus('Could not fetch address automatically', 'warning');
|
// Don't show warning for automatic lookups
|
||||||
}
|
}
|
||||||
|
}).catch(error => {
|
||||||
|
console.error('Address lookup failed:', error);
|
||||||
|
addressInput.value = '';
|
||||||
});
|
});
|
||||||
|
|
||||||
// Focus on first name input
|
// Focus on first name input
|
||||||
@ -633,7 +636,12 @@ function editLocation(locationId) {
|
|||||||
addressInput.value = result.formattedAddress || result.fullAddress;
|
addressInput.value = result.formattedAddress || result.fullAddress;
|
||||||
} else if (!location['Address']) {
|
} else if (!location['Address']) {
|
||||||
addressInput.value = '';
|
addressInput.value = '';
|
||||||
|
// Don't show error - just silently fail
|
||||||
}
|
}
|
||||||
|
}).catch(error => {
|
||||||
|
// Handle any unexpected errors
|
||||||
|
console.error('Address lookup failed:', error);
|
||||||
|
addressInput.value = '';
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -923,53 +931,20 @@ window.addEventListener('beforeunload', () => {
|
|||||||
// Reverse geocode to get address from coordinates
|
// Reverse geocode to get address from coordinates
|
||||||
async function reverseGeocode(lat, lng) {
|
async function reverseGeocode(lat, lng) {
|
||||||
try {
|
try {
|
||||||
// Add a small delay to respect rate limits
|
const response = await fetch(`/api/geocode/reverse?lat=${lat}&lng=${lng}`);
|
||||||
await new Promise(resolve => setTimeout(resolve, 1000));
|
|
||||||
|
|
||||||
const response = await fetch(`https://nominatim.openstreetmap.org/reverse?format=json&lat=${lat}&lon=${lng}&zoom=18&addressdetails=1`, {
|
|
||||||
headers: {
|
|
||||||
'User-Agent': 'NocoDB Map Viewer 1.0'
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
if (!response.ok) {
|
if (!response.ok) {
|
||||||
throw new Error('Geocoding service unavailable');
|
const error = await response.json();
|
||||||
|
throw new Error(error.error || 'Geocoding service unavailable');
|
||||||
}
|
}
|
||||||
|
|
||||||
const data = await response.json();
|
const result = await response.json();
|
||||||
|
|
||||||
if (data.error) {
|
if (!result.success || !result.data) {
|
||||||
throw new Error(data.error);
|
throw new Error('Geocoding failed');
|
||||||
}
|
}
|
||||||
|
|
||||||
// Format the address from the response
|
return result.data;
|
||||||
const address = data.display_name || '';
|
|
||||||
|
|
||||||
// You can also extract specific components if needed
|
|
||||||
const addressComponents = {
|
|
||||||
house_number: data.address?.house_number || '',
|
|
||||||
road: data.address?.road || '',
|
|
||||||
suburb: data.address?.suburb || data.address?.neighbourhood || '',
|
|
||||||
city: data.address?.city || data.address?.town || data.address?.village || '',
|
|
||||||
state: data.address?.state || '',
|
|
||||||
postcode: data.address?.postcode || '',
|
|
||||||
country: data.address?.country || ''
|
|
||||||
};
|
|
||||||
|
|
||||||
// Create a formatted address string
|
|
||||||
let formattedAddress = '';
|
|
||||||
if (addressComponents.house_number) formattedAddress += addressComponents.house_number + ' ';
|
|
||||||
if (addressComponents.road) formattedAddress += addressComponents.road + ', ';
|
|
||||||
if (addressComponents.suburb) formattedAddress += addressComponents.suburb + ', ';
|
|
||||||
if (addressComponents.city) formattedAddress += addressComponents.city + ', ';
|
|
||||||
if (addressComponents.state) formattedAddress += addressComponents.state + ' ';
|
|
||||||
if (addressComponents.postcode) formattedAddress += addressComponents.postcode;
|
|
||||||
|
|
||||||
return {
|
|
||||||
fullAddress: address,
|
|
||||||
formattedAddress: formattedAddress.trim().replace(/,$/, ''),
|
|
||||||
components: addressComponents
|
|
||||||
};
|
|
||||||
|
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.error('Reverse geocoding error:', error);
|
console.error('Reverse geocoding error:', error);
|
||||||
@ -977,45 +952,73 @@ async function reverseGeocode(lat, lng) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Add a new function for forward geocoding (address to coordinates)
|
||||||
|
async function forwardGeocode(address) {
|
||||||
|
try {
|
||||||
|
const response = await fetch(`/api/geocode/forward?address=${encodeURIComponent(address)}`);
|
||||||
|
|
||||||
|
if (!response.ok) {
|
||||||
|
const error = await response.json();
|
||||||
|
throw new Error(error.error || 'Geocoding service unavailable');
|
||||||
|
}
|
||||||
|
|
||||||
|
const result = await response.json();
|
||||||
|
|
||||||
|
if (!result.success || !result.data) {
|
||||||
|
throw new Error('Geocoding failed');
|
||||||
|
}
|
||||||
|
|
||||||
|
return result.data;
|
||||||
|
|
||||||
|
} catch (error) {
|
||||||
|
console.error('Forward geocoding error:', error);
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// Manual address lookup for add form
|
// Manual address lookup for add form
|
||||||
function lookupAddressForAdd() {
|
async function lookupAddressForAdd() {
|
||||||
const lat = parseFloat(document.getElementById('location-lat').value);
|
const latInput = document.getElementById('location-lat');
|
||||||
const lng = parseFloat(document.getElementById('location-lng').value);
|
const lngInput = document.getElementById('location-lng');
|
||||||
const addressInput = document.getElementById('location-address');
|
const addressInput = document.getElementById('location-address');
|
||||||
|
|
||||||
|
const lat = parseFloat(latInput.value);
|
||||||
|
const lng = parseFloat(lngInput.value);
|
||||||
|
|
||||||
if (!isNaN(lat) && !isNaN(lng)) {
|
if (!isNaN(lat) && !isNaN(lng)) {
|
||||||
addressInput.value = 'Looking up address...';
|
addressInput.value = 'Looking up address...';
|
||||||
reverseGeocode(lat, lng).then(result => {
|
const result = await reverseGeocode(lat, lng);
|
||||||
if (result) {
|
if (result) {
|
||||||
addressInput.value = result.formattedAddress || result.fullAddress;
|
addressInput.value = result.formattedAddress || result.fullAddress;
|
||||||
showStatus('Address found!', 'success');
|
showStatus('Address found!', 'success');
|
||||||
} else {
|
} else {
|
||||||
addressInput.value = '';
|
addressInput.value = '';
|
||||||
showStatus('Could not find address for these coordinates', 'error');
|
showStatus('Could not find address for these coordinates', 'warning');
|
||||||
}
|
}
|
||||||
});
|
|
||||||
} else {
|
} else {
|
||||||
showStatus('Please enter valid coordinates first', 'warning');
|
showStatus('Please enter valid coordinates first', 'warning');
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Manual address lookup for edit form
|
// Manual address lookup for edit form
|
||||||
function lookupAddressForEdit() {
|
async function lookupAddressForEdit() {
|
||||||
const lat = parseFloat(document.getElementById('edit-location-lat').value);
|
const latInput = document.getElementById('edit-location-lat');
|
||||||
const lng = parseFloat(document.getElementById('edit-location-lng').value);
|
const lngInput = document.getElementById('edit-location-lng');
|
||||||
const addressInput = document.getElementById('edit-location-address');
|
const addressInput = document.getElementById('edit-location-address');
|
||||||
|
|
||||||
|
const lat = parseFloat(latInput.value);
|
||||||
|
const lng = parseFloat(lngInput.value);
|
||||||
|
|
||||||
if (!isNaN(lat) && !isNaN(lng)) {
|
if (!isNaN(lat) && !isNaN(lng)) {
|
||||||
addressInput.value = 'Looking up address...';
|
addressInput.value = 'Looking up address...';
|
||||||
reverseGeocode(lat, lng).then(result => {
|
const result = await reverseGeocode(lat, lng);
|
||||||
if (result) {
|
if (result) {
|
||||||
addressInput.value = result.formattedAddress || result.fullAddress;
|
addressInput.value = result.formattedAddress || result.fullAddress;
|
||||||
showStatus('Address found!', 'success');
|
showStatus('Address found!', 'success');
|
||||||
} else {
|
} else {
|
||||||
addressInput.value = '';
|
addressInput.value = '';
|
||||||
showStatus('Could not find address for these coordinates', 'error');
|
showStatus('Could not find address for these coordinates', 'warning');
|
||||||
}
|
}
|
||||||
});
|
|
||||||
} else {
|
} else {
|
||||||
showStatus('Please enter valid coordinates first', 'warning');
|
showStatus('Please enter valid coordinates first', 'warning');
|
||||||
}
|
}
|
||||||
|
|||||||
113
nocodb-map-viewer/app/routes/geocoding.js
Normal file
113
nocodb-map-viewer/app/routes/geocoding.js
Normal file
@ -0,0 +1,113 @@
|
|||||||
|
const express = require('express');
|
||||||
|
const router = express.Router();
|
||||||
|
const rateLimit = require('express-rate-limit');
|
||||||
|
const { reverseGeocode, forwardGeocode, getCacheStats } = require('../services/geocoding');
|
||||||
|
|
||||||
|
// Rate limiter specifically for geocoding endpoints
|
||||||
|
const geocodeLimiter = rateLimit({
|
||||||
|
windowMs: 15 * 60 * 1000, // 15 minutes
|
||||||
|
max: 30, // limit each IP to 30 requests per windowMs
|
||||||
|
message: 'Too many geocoding requests, please try again later.'
|
||||||
|
});
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Reverse geocode endpoint
|
||||||
|
* GET /api/geocode/reverse?lat=<latitude>&lng=<longitude>
|
||||||
|
*/
|
||||||
|
router.get('/reverse', geocodeLimiter, async (req, res) => {
|
||||||
|
try {
|
||||||
|
const { lat, lng } = req.query;
|
||||||
|
|
||||||
|
// Validate input
|
||||||
|
if (!lat || !lng) {
|
||||||
|
return res.status(400).json({
|
||||||
|
success: false,
|
||||||
|
error: 'Latitude and longitude are required'
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
const latitude = parseFloat(lat);
|
||||||
|
const longitude = parseFloat(lng);
|
||||||
|
|
||||||
|
if (isNaN(latitude) || isNaN(longitude)) {
|
||||||
|
return res.status(400).json({
|
||||||
|
success: false,
|
||||||
|
error: 'Invalid latitude or longitude'
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
if (latitude < -90 || latitude > 90 || longitude < -180 || longitude > 180) {
|
||||||
|
return res.status(400).json({
|
||||||
|
success: false,
|
||||||
|
error: 'Coordinates out of range'
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
// Perform reverse geocoding
|
||||||
|
const result = await reverseGeocode(latitude, longitude);
|
||||||
|
|
||||||
|
res.json({
|
||||||
|
success: true,
|
||||||
|
data: result
|
||||||
|
});
|
||||||
|
|
||||||
|
} catch (error) {
|
||||||
|
console.error('Reverse geocoding error:', error);
|
||||||
|
|
||||||
|
const statusCode = error.message.includes('Rate limit') ? 429 : 500;
|
||||||
|
|
||||||
|
res.status(statusCode).json({
|
||||||
|
success: false,
|
||||||
|
error: error.message
|
||||||
|
});
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Forward geocode endpoint
|
||||||
|
* GET /api/geocode/forward?address=<address>
|
||||||
|
*/
|
||||||
|
router.get('/forward', geocodeLimiter, async (req, res) => {
|
||||||
|
try {
|
||||||
|
const { address } = req.query;
|
||||||
|
|
||||||
|
// Validate input
|
||||||
|
if (!address || address.trim().length === 0) {
|
||||||
|
return res.status(400).json({
|
||||||
|
success: false,
|
||||||
|
error: 'Address is required'
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
// Perform forward geocoding
|
||||||
|
const result = await forwardGeocode(address);
|
||||||
|
|
||||||
|
res.json({
|
||||||
|
success: true,
|
||||||
|
data: result
|
||||||
|
});
|
||||||
|
|
||||||
|
} catch (error) {
|
||||||
|
console.error('Forward geocoding error:', error);
|
||||||
|
|
||||||
|
const statusCode = error.message.includes('Rate limit') ? 429 : 500;
|
||||||
|
|
||||||
|
res.status(statusCode).json({
|
||||||
|
success: false,
|
||||||
|
error: error.message
|
||||||
|
});
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get geocoding cache statistics (admin endpoint)
|
||||||
|
* GET /api/geocode/cache/stats
|
||||||
|
*/
|
||||||
|
router.get('/cache/stats', (req, res) => {
|
||||||
|
res.json({
|
||||||
|
success: true,
|
||||||
|
data: getCacheStats()
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
module.exports = router;
|
||||||
@ -7,6 +7,9 @@ const winston = require('winston');
|
|||||||
const path = require('path');
|
const path = require('path');
|
||||||
require('dotenv').config();
|
require('dotenv').config();
|
||||||
|
|
||||||
|
// Import geocoding routes
|
||||||
|
const geocodingRoutes = require('./routes/geocoding');
|
||||||
|
|
||||||
// Parse project and table IDs from view URL
|
// Parse project and table IDs from view URL
|
||||||
function parseNocoDBUrl(url) {
|
function parseNocoDBUrl(url) {
|
||||||
if (!url) return { projectId: null, tableId: null };
|
if (!url) return { projectId: null, tableId: null };
|
||||||
@ -142,6 +145,9 @@ app.use(express.json({ limit: '10mb' }));
|
|||||||
app.use(express.static(path.join(__dirname, 'public')));
|
app.use(express.static(path.join(__dirname, 'public')));
|
||||||
app.use('/api/', limiter);
|
app.use('/api/', limiter);
|
||||||
|
|
||||||
|
// Add geocoding routes
|
||||||
|
app.use('/api/geocode', geocodingRoutes);
|
||||||
|
|
||||||
// Health check endpoint
|
// Health check endpoint
|
||||||
app.get('/health', (req, res) => {
|
app.get('/health', (req, res) => {
|
||||||
res.json({
|
res.json({
|
||||||
|
|||||||
231
nocodb-map-viewer/app/services/geocoding.js
Normal file
231
nocodb-map-viewer/app/services/geocoding.js
Normal file
@ -0,0 +1,231 @@
|
|||||||
|
const axios = require('axios');
|
||||||
|
const winston = require('winston');
|
||||||
|
|
||||||
|
// Configure logger
|
||||||
|
const logger = winston.createLogger({
|
||||||
|
level: process.env.NODE_ENV === 'production' ? 'info' : 'debug',
|
||||||
|
format: winston.format.combine(
|
||||||
|
winston.format.timestamp(),
|
||||||
|
winston.format.json()
|
||||||
|
),
|
||||||
|
transports: [
|
||||||
|
new winston.transports.Console({
|
||||||
|
format: winston.format.simple()
|
||||||
|
})
|
||||||
|
]
|
||||||
|
});
|
||||||
|
|
||||||
|
// Cache for geocoding results (simple in-memory cache)
|
||||||
|
const geocodeCache = new Map();
|
||||||
|
const CACHE_TTL = 24 * 60 * 60 * 1000; // 24 hours
|
||||||
|
|
||||||
|
// Clean up old cache entries periodically
|
||||||
|
setInterval(() => {
|
||||||
|
const now = Date.now();
|
||||||
|
for (const [key, value] of geocodeCache.entries()) {
|
||||||
|
if (now - value.timestamp > CACHE_TTL) {
|
||||||
|
geocodeCache.delete(key);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}, 60 * 60 * 1000); // Run every hour
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Reverse geocode coordinates to get address
|
||||||
|
* @param {number} lat - Latitude
|
||||||
|
* @param {number} lng - Longitude
|
||||||
|
* @returns {Promise<Object>} Geocoding result
|
||||||
|
*/
|
||||||
|
async function reverseGeocode(lat, lng) {
|
||||||
|
// Create cache key
|
||||||
|
const cacheKey = `${lat.toFixed(6)},${lng.toFixed(6)}`;
|
||||||
|
|
||||||
|
// Check cache first
|
||||||
|
const cached = geocodeCache.get(cacheKey);
|
||||||
|
if (cached && Date.now() - cached.timestamp < CACHE_TTL) {
|
||||||
|
logger.debug(`Geocoding cache hit for ${cacheKey}`);
|
||||||
|
return cached.data;
|
||||||
|
}
|
||||||
|
|
||||||
|
try {
|
||||||
|
// Add delay to respect Nominatim rate limits (max 1 request per second)
|
||||||
|
await new Promise(resolve => setTimeout(resolve, 1000));
|
||||||
|
|
||||||
|
logger.info(`Reverse geocoding: ${lat}, ${lng}`);
|
||||||
|
|
||||||
|
const response = await axios.get('https://nominatim.openstreetmap.org/reverse', {
|
||||||
|
params: {
|
||||||
|
format: 'json',
|
||||||
|
lat: lat,
|
||||||
|
lon: lng,
|
||||||
|
zoom: 18,
|
||||||
|
addressdetails: 1,
|
||||||
|
'accept-language': 'en'
|
||||||
|
},
|
||||||
|
headers: {
|
||||||
|
'User-Agent': 'NocoDB Map Viewer 1.0 (https://github.com/yourusername/nocodb-map-viewer)'
|
||||||
|
},
|
||||||
|
timeout: 10000
|
||||||
|
});
|
||||||
|
|
||||||
|
if (response.data.error) {
|
||||||
|
throw new Error(response.data.error);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Process the response
|
||||||
|
const result = processGeocodeResponse(response.data);
|
||||||
|
|
||||||
|
// Cache the result
|
||||||
|
geocodeCache.set(cacheKey, {
|
||||||
|
data: result,
|
||||||
|
timestamp: Date.now()
|
||||||
|
});
|
||||||
|
|
||||||
|
return result;
|
||||||
|
|
||||||
|
} catch (error) {
|
||||||
|
logger.error('Reverse geocoding error:', error.message);
|
||||||
|
|
||||||
|
if (error.response?.status === 429) {
|
||||||
|
throw new Error('Rate limit exceeded. Please try again later.');
|
||||||
|
} else if (error.code === 'ECONNABORTED') {
|
||||||
|
throw new Error('Geocoding service timeout');
|
||||||
|
} else {
|
||||||
|
throw new Error('Geocoding service unavailable');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Forward geocode address to get coordinates
|
||||||
|
* @param {string} address - Address to geocode
|
||||||
|
* @returns {Promise<Object>} Geocoding result
|
||||||
|
*/
|
||||||
|
async function forwardGeocode(address) {
|
||||||
|
// Create cache key
|
||||||
|
const cacheKey = `addr:${address.toLowerCase()}`;
|
||||||
|
|
||||||
|
// Check cache first
|
||||||
|
const cached = geocodeCache.get(cacheKey);
|
||||||
|
if (cached && Date.now() - cached.timestamp < CACHE_TTL) {
|
||||||
|
logger.debug(`Geocoding cache hit for ${cacheKey}`);
|
||||||
|
return cached.data;
|
||||||
|
}
|
||||||
|
|
||||||
|
try {
|
||||||
|
// Add delay to respect rate limits
|
||||||
|
await new Promise(resolve => setTimeout(resolve, 1000));
|
||||||
|
|
||||||
|
logger.info(`Forward geocoding: ${address}`);
|
||||||
|
|
||||||
|
const response = await axios.get('https://nominatim.openstreetmap.org/search', {
|
||||||
|
params: {
|
||||||
|
format: 'json',
|
||||||
|
q: address,
|
||||||
|
limit: 1,
|
||||||
|
addressdetails: 1,
|
||||||
|
'accept-language': 'en'
|
||||||
|
},
|
||||||
|
headers: {
|
||||||
|
'User-Agent': 'NocoDB Map Viewer 1.0 (https://github.com/yourusername/nocodb-map-viewer)'
|
||||||
|
},
|
||||||
|
timeout: 10000
|
||||||
|
});
|
||||||
|
|
||||||
|
if (!response.data || response.data.length === 0) {
|
||||||
|
throw new Error('No results found');
|
||||||
|
}
|
||||||
|
|
||||||
|
// Process the first result
|
||||||
|
const result = processGeocodeResponse(response.data[0]);
|
||||||
|
|
||||||
|
// Cache the result
|
||||||
|
geocodeCache.set(cacheKey, {
|
||||||
|
data: result,
|
||||||
|
timestamp: Date.now()
|
||||||
|
});
|
||||||
|
|
||||||
|
return result;
|
||||||
|
|
||||||
|
} catch (error) {
|
||||||
|
logger.error('Forward geocoding error:', error.message);
|
||||||
|
|
||||||
|
if (error.response?.status === 429) {
|
||||||
|
throw new Error('Rate limit exceeded. Please try again later.');
|
||||||
|
} else if (error.code === 'ECONNABORTED') {
|
||||||
|
throw new Error('Geocoding service timeout');
|
||||||
|
} else {
|
||||||
|
throw new Error('Geocoding service unavailable');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Process geocoding response into standardized format
|
||||||
|
* @param {Object} data - Raw geocoding response
|
||||||
|
* @returns {Object} Processed geocoding data
|
||||||
|
*/
|
||||||
|
function processGeocodeResponse(data) {
|
||||||
|
// Extract address components
|
||||||
|
const addressComponents = {
|
||||||
|
house_number: data.address?.house_number || '',
|
||||||
|
road: data.address?.road || '',
|
||||||
|
suburb: data.address?.suburb || data.address?.neighbourhood || '',
|
||||||
|
city: data.address?.city || data.address?.town || data.address?.village || '',
|
||||||
|
state: data.address?.state || data.address?.province || '',
|
||||||
|
postcode: data.address?.postcode || '',
|
||||||
|
country: data.address?.country || ''
|
||||||
|
};
|
||||||
|
|
||||||
|
// Create formatted address string
|
||||||
|
let formattedAddress = '';
|
||||||
|
if (addressComponents.house_number) formattedAddress += addressComponents.house_number + ' ';
|
||||||
|
if (addressComponents.road) formattedAddress += addressComponents.road + ', ';
|
||||||
|
if (addressComponents.suburb) formattedAddress += addressComponents.suburb + ', ';
|
||||||
|
if (addressComponents.city) formattedAddress += addressComponents.city + ', ';
|
||||||
|
if (addressComponents.state) formattedAddress += addressComponents.state + ' ';
|
||||||
|
if (addressComponents.postcode) formattedAddress += addressComponents.postcode;
|
||||||
|
|
||||||
|
// Clean up formatting
|
||||||
|
formattedAddress = formattedAddress.trim().replace(/,$/, '');
|
||||||
|
|
||||||
|
return {
|
||||||
|
fullAddress: data.display_name || '',
|
||||||
|
formattedAddress: formattedAddress,
|
||||||
|
components: addressComponents,
|
||||||
|
coordinates: {
|
||||||
|
lat: parseFloat(data.lat),
|
||||||
|
lng: parseFloat(data.lon)
|
||||||
|
},
|
||||||
|
boundingBox: data.boundingbox || null,
|
||||||
|
placeId: data.place_id || null,
|
||||||
|
osmType: data.osm_type || null,
|
||||||
|
osmId: data.osm_id || null
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get cache statistics
|
||||||
|
* @returns {Object} Cache statistics
|
||||||
|
*/
|
||||||
|
function getCacheStats() {
|
||||||
|
return {
|
||||||
|
size: geocodeCache.size,
|
||||||
|
maxSize: 1000, // Could be made configurable
|
||||||
|
ttl: CACHE_TTL
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Clear the geocoding cache
|
||||||
|
*/
|
||||||
|
function clearCache() {
|
||||||
|
geocodeCache.clear();
|
||||||
|
logger.info('Geocoding cache cleared');
|
||||||
|
}
|
||||||
|
|
||||||
|
module.exports = {
|
||||||
|
reverseGeocode,
|
||||||
|
forwardGeocode,
|
||||||
|
getCacheStats,
|
||||||
|
clearCache
|
||||||
|
};
|
||||||
Loading…
x
Reference in New Issue
Block a user