Appendix C: Web GIS APIs and Services#
This appendix provides a comprehensive reference for popular Web GIS APIs and services commonly used in modern geospatial applications. Each service is described with authentication methods, key endpoints, usage examples, and integration patterns.
Map Services#
OpenStreetMap-based Services#
OpenStreetMap Tile Service#
Description: The standard OpenStreetMap tile service provides free access to OSM map tiles.
Base URLs:
https://tile.openstreetmap.org/{z}/{x}/{y}.pnghttps://a.tile.openstreetmap.org/{z}/{x}/{y}.png(load balanced)
Features:
Free to use with attribution
Standard raster tiles (256x256 pixels)
Zoom levels 0-19
Web Mercator projection (EPSG:3857)
Usage Policy:
Maximum 2 requests per second
Must include attribution
Not for heavy commercial use
JavaScript Example:
// Adding OSM tiles to MapLibre GL JS
const map = new maplibregl.Map({
container: "map",
style: {
version: 8,
sources: {
osm: {
type: "raster",
tiles: [
"https://a.tile.openstreetmap.org/{z}/{x}/{y}.png",
"https://b.tile.openstreetmap.org/{z}/{x}/{y}.png",
"https://c.tile.openstreetmap.org/{z}/{x}/{y}.png",
],
tileSize: 256,
attribution: "© OpenStreetMap contributors",
},
},
layers: [
{
id: "osm",
source: "osm",
type: "raster",
},
],
},
});
Overpass API#
Description: Overpass API provides read-only access to OpenStreetMap data with powerful query capabilities.
Base URL: https://overpass-api.de/api/interpreter
Query Language: Overpass QL (Query Language)
Example Queries:
class OverpassAPI {
constructor(baseUrl = "https://overpass-api.de/api/interpreter") {
this.baseUrl = baseUrl;
}
async query(overpassQuery, format = "json") {
const params = new URLSearchParams({
data: overpassQuery,
});
const response = await fetch(this.baseUrl, {
method: "POST",
body: params,
headers: {
"Content-Type": "application/x-www-form-urlencoded",
},
});
if (!response.ok) {
throw new Error(`Overpass API error: ${response.statusText}`);
}
return response.json();
}
// Get all restaurants in a bounding box
async getRestaurants(bbox) {
const [south, west, north, east] = bbox;
const query = `
[out:json][timeout:25];
(
node["amenity"="restaurant"](${south},${west},${north},${east});
way["amenity"="restaurant"](${south},${west},${north},${east});
relation["amenity"="restaurant"](${south},${west},${north},${east});
);
out center meta;
`;
return this.query(query);
}
// Get all amenities near a point
async getAmenitiesNearPoint(lat, lng, radius = 1000) {
const query = `
[out:json][timeout:25];
(
node["amenity"](around:${radius},${lat},${lng});
way["amenity"](around:${radius},${lat},${lng});
);
out center meta;
`;
return this.query(query);
}
// Convert Overpass result to GeoJSON
overpassToGeoJSON(overpassResult) {
const features = overpassResult.elements
.map((element) => {
let geometry;
if (element.type === "node") {
geometry = {
type: "Point",
coordinates: [element.lon, element.lat],
};
} else if (element.type === "way") {
const coordinates = element.geometry
? element.geometry.map((node) => [node.lon, node.lat])
: [];
geometry = {
type: "LineString",
coordinates: coordinates,
};
} else {
// Handle relations and other complex geometries
return null;
}
return {
type: "Feature",
properties: element.tags || {},
geometry: geometry,
};
})
.filter(Boolean);
return {
type: "FeatureCollection",
features: features,
};
}
}
// Usage example
const overpass = new OverpassAPI();
// Get restaurants in San Francisco
const restaurants = await overpass.getRestaurants([
37.7749, -122.4194, 37.7849, -122.4094,
]);
const geoJSON = overpass.overpassToGeoJSON(restaurants);
Commercial Map Services#
Mapbox#
Description: Mapbox provides comprehensive mapping platform with tiles, geocoding, routing, and more.
Authentication: API Token (Bearer token)
Base URL: https://api.mapbox.com
Key Services:
class MapboxAPI {
constructor(accessToken) {
this.accessToken = accessToken;
this.baseUrl = "https://api.mapbox.com";
}
// Geocoding API
async geocode(query, options = {}) {
const params = new URLSearchParams({
access_token: this.accessToken,
limit: options.limit || 5,
types: options.types || "",
...options,
});
const response = await fetch(
`${this.baseUrl}/geocoding/v5/mapbox.places/${encodeURIComponent(
query
)}.json?${params}`
);
return response.json();
}
// Reverse Geocoding
async reverseGeocode(lng, lat, options = {}) {
const params = new URLSearchParams({
access_token: this.accessToken,
types: options.types || "",
...options,
});
const response = await fetch(
`${this.baseUrl}/geocoding/v5/mapbox.places/${lng},${lat}.json?${params}`
);
return response.json();
}
// Directions API
async getDirections(coordinates, profile = "driving", options = {}) {
const coordString = coordinates.map((coord) => coord.join(",")).join(";");
const params = new URLSearchParams({
access_token: this.accessToken,
geometries: options.geometries || "geojson",
steps: options.steps || "true",
...options,
});
const response = await fetch(
`${this.baseUrl}/directions/v5/mapbox/${profile}/${coordString}?${params}`
);
return response.json();
}
// Isochrone API
async getIsochrone(lng, lat, contours, profile = "driving") {
const params = new URLSearchParams({
access_token: this.accessToken,
contours_minutes: contours.join(","),
generalize: "100",
});
const response = await fetch(
`${this.baseUrl}/isochrone/v1/mapbox/${profile}/${lng},${lat}?${params}`
);
return response.json();
}
// Matrix API (travel times between points)
async getMatrix(coordinates, profile = "driving") {
const coordString = coordinates.map((coord) => coord.join(",")).join(";");
const params = new URLSearchParams({
access_token: this.accessToken,
sources: "all",
destinations: "all",
});
const response = await fetch(
`${this.baseUrl}/directions-matrix/v1/mapbox/${profile}/${coordString}?${params}`
);
return response.json();
}
// Static Images API
getStaticImageUrl(style, lng, lat, zoom, options = {}) {
const overlay = options.overlay || "";
const retina = options.retina ? "@2x" : "";
const width = options.width || 600;
const height = options.height || 400;
return `${this.baseUrl}/styles/v1/mapbox/${style}/static/${overlay}/${lng},${lat},${zoom}/${width}x${height}${retina}?access_token=${this.accessToken}`;
}
}
// Usage examples
const mapbox = new MapboxAPI("your-access-token");
// Geocoding
const locations = await mapbox.geocode("San Francisco, CA");
// Get driving directions
const directions = await mapbox.getDirections(
[
[-122.4194, 37.7749], // San Francisco
[-122.0839, 37.422], // Palo Alto
],
"driving"
);
// Get 15-minute isochrone
const isochrone = await mapbox.getIsochrone(
-122.4194,
37.7749,
[15],
"walking"
);
Google Maps Platform#
Description: Google’s comprehensive suite of mapping and location services.
Authentication: API Key
Base URL: https://maps.googleapis.com/maps/api
class GoogleMapsAPI {
constructor(apiKey) {
this.apiKey = apiKey;
this.baseUrl = "https://maps.googleapis.com/maps/api";
}
// Geocoding API
async geocode(address) {
const params = new URLSearchParams({
address: address,
key: this.apiKey,
});
const response = await fetch(`${this.baseUrl}/geocode/json?${params}`);
return response.json();
}
// Reverse Geocoding
async reverseGeocode(lat, lng) {
const params = new URLSearchParams({
latlng: `${lat},${lng}`,
key: this.apiKey,
});
const response = await fetch(`${this.baseUrl}/geocode/json?${params}`);
return response.json();
}
// Places API - Nearby Search
async nearbySearch(lat, lng, radius, type) {
const params = new URLSearchParams({
location: `${lat},${lng}`,
radius: radius,
type: type,
key: this.apiKey,
});
const response = await fetch(
`${this.baseUrl}/place/nearbysearch/json?${params}`
);
return response.json();
}
// Places API - Text Search
async textSearch(query) {
const params = new URLSearchParams({
query: query,
key: this.apiKey,
});
const response = await fetch(
`${this.baseUrl}/place/textsearch/json?${params}`
);
return response.json();
}
// Directions API
async getDirections(origin, destination, mode = "driving") {
const params = new URLSearchParams({
origin: `${origin[0]},${origin[1]}`,
destination: `${destination[0]},${destination[1]}`,
mode: mode,
key: this.apiKey,
});
const response = await fetch(`${this.baseUrl}/directions/json?${params}`);
return response.json();
}
// Distance Matrix API
async getDistanceMatrix(origins, destinations, mode = "driving") {
const originString = origins.map((o) => `${o[0]},${o[1]}`).join("|");
const destString = destinations.map((d) => `${d[0]},${d[1]}`).join("|");
const params = new URLSearchParams({
origins: originString,
destinations: destString,
mode: mode,
key: this.apiKey,
});
const response = await fetch(
`${this.baseUrl}/distancematrix/json?${params}`
);
return response.json();
}
// Elevation API
async getElevation(locations) {
const locationString = locations.map((l) => `${l[0]},${l[1]}`).join("|");
const params = new URLSearchParams({
locations: locationString,
key: this.apiKey,
});
const response = await fetch(`${this.baseUrl}/elevation/json?${params}`);
return response.json();
}
// Static Maps API
getStaticMapUrl(center, zoom, size, options = {}) {
const params = new URLSearchParams({
center: `${center[0]},${center[1]}`,
zoom: zoom,
size: `${size[0]}x${size[1]}`,
key: this.apiKey,
...options,
});
return `${this.baseUrl}/staticmap?${params}`;
}
}
// Usage examples
const gmaps = new GoogleMapsAPI("your-api-key");
// Find nearby restaurants
const restaurants = await gmaps.nearbySearch(
37.7749,
-122.4194,
1000,
"restaurant"
);
// Get elevation data
const elevation = await gmaps.getElevation([
[37.7749, -122.4194],
[37.7849, -122.4094],
]);
Geocoding Services#
Nominatim (OpenStreetMap)#
Description: Free geocoding service based on OpenStreetMap data.
Base URL: https://nominatim.openstreetmap.org
class NominatimAPI {
constructor(baseUrl = "https://nominatim.openstreetmap.org") {
this.baseUrl = baseUrl;
this.userAgent = "WebGIS-App/1.0"; // Always set a user agent
}
async search(query, options = {}) {
const params = new URLSearchParams({
q: query,
format: "json",
limit: options.limit || 5,
countrycodes: options.countrycodes || "",
...options,
});
const response = await fetch(`${this.baseUrl}/search?${params}`, {
headers: {
"User-Agent": this.userAgent,
},
});
return response.json();
}
async reverse(lat, lng, options = {}) {
const params = new URLSearchParams({
lat: lat,
lon: lng,
format: "json",
...options,
});
const response = await fetch(`${this.baseUrl}/reverse?${params}`, {
headers: {
"User-Agent": this.userAgent,
},
});
return response.json();
}
// Convert Nominatim result to GeoJSON
nominatimToGeoJSON(results) {
const features = results.map((result) => ({
type: "Feature",
properties: {
place_id: result.place_id,
display_name: result.display_name,
class: result.class,
type: result.type,
importance: result.importance,
},
geometry: {
type: "Point",
coordinates: [parseFloat(result.lon), parseFloat(result.lat)],
},
}));
return {
type: "FeatureCollection",
features: features,
};
}
}
// Usage
const nominatim = new NominatimAPI();
const results = await nominatim.search("San Francisco, CA");
const geoJSON = nominatim.nominatimToGeoJSON(results);
Pelias#
Description: Open-source geocoding engine that can be self-hosted or used via third-party services.
class PeliasAPI {
constructor(baseUrl) {
this.baseUrl = baseUrl;
}
async search(text, options = {}) {
const params = new URLSearchParams({
text: text,
size: options.size || 10,
...options,
});
const response = await fetch(`${this.baseUrl}/v1/search?${params}`);
return response.json();
}
async autocomplete(text, options = {}) {
const params = new URLSearchParams({
text: text,
size: options.size || 10,
...options,
});
const response = await fetch(`${this.baseUrl}/v1/autocomplete?${params}`);
return response.json();
}
async reverse(lat, lng, options = {}) {
const params = new URLSearchParams({
"point.lat": lat,
"point.lon": lng,
size: options.size || 10,
...options,
});
const response = await fetch(`${this.baseUrl}/v1/reverse?${params}`);
return response.json();
}
async structured(components) {
const params = new URLSearchParams(components);
const response = await fetch(
`${this.baseUrl}/v1/search/structured?${params}`
);
return response.json();
}
}
Routing Services#
OpenRouteService#
Description: Open-source routing service supporting multiple profiles and services.
Base URL: https://api.openrouteservice.org
class OpenRouteServiceAPI {
constructor(apiKey) {
this.apiKey = apiKey;
this.baseUrl = "https://api.openrouteservice.org";
}
async getDirections(coordinates, profile = "driving-car", options = {}) {
const body = {
coordinates: coordinates,
format: "geojson",
...options,
};
const response = await fetch(`${this.baseUrl}/v2/directions/${profile}`, {
method: "POST",
headers: {
Authorization: this.apiKey,
"Content-Type": "application/json",
},
body: JSON.stringify(body),
});
return response.json();
}
async getIsochrones(locations, profile = "driving-car", range = [600]) {
const body = {
locations: locations,
range: range,
range_type: "time",
};
const response = await fetch(`${this.baseUrl}/v2/isochrones/${profile}`, {
method: "POST",
headers: {
Authorization: this.apiKey,
"Content-Type": "application/json",
},
body: JSON.stringify(body),
});
return response.json();
}
async getMatrix(locations, profile = "driving-car", metrics = ["duration"]) {
const body = {
locations: locations,
metrics: metrics,
resolve_locations: true,
};
const response = await fetch(`${this.baseUrl}/v2/matrix/${profile}`, {
method: "POST",
headers: {
Authorization: this.apiKey,
"Content-Type": "application/json",
},
body: JSON.stringify(body),
});
return response.json();
}
}
// Usage
const ors = new OpenRouteServiceAPI("your-api-key");
// Get driving directions
const directions = await ors.getDirections(
[
[-122.4194, 37.7749],
[-122.0839, 37.422],
],
"driving-car"
);
// Get 10-minute walking isochrone
const isochrone = await ors.getIsochrones(
[[-122.4194, 37.7749]],
"foot-walking",
[600]
);
GraphHopper#
Description: Fast and memory-efficient routing engine with multiple transportation modes.
class GraphHopperAPI {
constructor(apiKey) {
this.apiKey = apiKey;
this.baseUrl = "https://graphhopper.com/api/1";
}
async getRoute(points, vehicle = "car", options = {}) {
const params = new URLSearchParams({
key: this.apiKey,
vehicle: vehicle,
points_encoded: false,
...options,
});
// Add points to params
points.forEach((point) => {
params.append("point", `${point[1]},${point[0]}`);
});
const response = await fetch(`${this.baseUrl}/route?${params}`);
return response.json();
}
async getMatrix(points, vehicle = "car") {
const body = {
points: points.map((p) => [p[1], p[0]]), // GraphHopper uses [lat, lng]
vehicle: vehicle,
};
const response = await fetch(`${this.baseUrl}/matrix?key=${this.apiKey}`, {
method: "POST",
headers: {
"Content-Type": "application/json",
},
body: JSON.stringify(body),
});
return response.json();
}
async getIsochrone(point, time_limit, vehicle = "car") {
const params = new URLSearchParams({
key: this.apiKey,
point: `${point[1]},${point[0]}`,
time_limit: time_limit,
vehicle: vehicle,
});
const response = await fetch(`${this.baseUrl}/isochrone?${params}`);
return response.json();
}
}
Weather and Environmental Data#
OpenWeatherMap#
Description: Weather data and forecasts with various data layers.
class OpenWeatherMapAPI {
constructor(apiKey) {
this.apiKey = apiKey;
this.baseUrl = "https://api.openweathermap.org/data/2.5";
}
async getCurrentWeather(lat, lon, units = "metric") {
const params = new URLSearchParams({
lat: lat,
lon: lon,
appid: this.apiKey,
units: units,
});
const response = await fetch(`${this.baseUrl}/weather?${params}`);
return response.json();
}
async getForecast(lat, lon, units = "metric") {
const params = new URLSearchParams({
lat: lat,
lon: lon,
appid: this.apiKey,
units: units,
});
const response = await fetch(`${this.baseUrl}/forecast?${params}`);
return response.json();
}
// Weather map tiles
getWeatherTileUrl(layer, zoom, x, y) {
return `https://tile.openweathermap.org/map/${layer}/${zoom}/${x}/${y}.png?appid=${this.apiKey}`;
}
// Convert weather data to GeoJSON
weatherToGeoJSON(weatherData) {
return {
type: "FeatureCollection",
features: [
{
type: "Feature",
geometry: {
type: "Point",
coordinates: [weatherData.coord.lon, weatherData.coord.lat],
},
properties: {
name: weatherData.name,
country: weatherData.sys.country,
weather: weatherData.weather[0],
temperature: weatherData.main.temp,
humidity: weatherData.main.humidity,
pressure: weatherData.main.pressure,
wind_speed: weatherData.wind?.speed,
timestamp: weatherData.dt,
},
},
],
};
}
}
Spatial Analysis Services#
PostGIS REST API Example#
class PostGISAPI {
constructor(baseUrl, apiKey) {
this.baseUrl = baseUrl;
this.apiKey = apiKey;
}
async spatialQuery(sql) {
const response = await fetch(`${this.baseUrl}/query`, {
method: "POST",
headers: {
Authorization: `Bearer ${this.apiKey}`,
"Content-Type": "application/json",
},
body: JSON.stringify({ sql }),
});
return response.json();
}
async buffer(geojson, distance, units = "meters") {
const response = await fetch(`${this.baseUrl}/buffer`, {
method: "POST",
headers: {
Authorization: `Bearer ${this.apiKey}`,
"Content-Type": "application/json",
},
body: JSON.stringify({
geometry: geojson,
distance: distance,
units: units,
}),
});
return response.json();
}
async intersects(geometry1, geometry2) {
const response = await fetch(`${this.baseUrl}/intersects`, {
method: "POST",
headers: {
Authorization: `Bearer ${this.apiKey}`,
"Content-Type": "application/json",
},
body: JSON.stringify({
geometry1: geometry1,
geometry2: geometry2,
}),
});
return response.json();
}
async nearestNeighbor(point, layer, limit = 10) {
const response = await fetch(`${this.baseUrl}/nearest`, {
method: "POST",
headers: {
Authorization: `Bearer ${this.apiKey}`,
"Content-Type": "application/json",
},
body: JSON.stringify({
point: point,
layer: layer,
limit: limit,
}),
});
return response.json();
}
}
Data Sources and Catalogs#
USGS APIs#
class USGSAPI {
constructor() {
this.baseUrl = "https://earthquake.usgs.gov/fdsnws/event/1";
}
async getEarthquakes(options = {}) {
const params = new URLSearchParams({
format: "geojson",
starttime: options.starttime || "2023-01-01",
endtime: options.endtime || new Date().toISOString().split("T")[0],
minmagnitude: options.minmagnitude || 2.5,
...options,
});
const response = await fetch(`${this.baseUrl}/query?${params}`);
return response.json();
}
async getEarthquakeDetails(eventId) {
const response = await fetch(
`${this.baseUrl}/query?eventid=${eventId}&format=geojson`
);
return response.json();
}
}
// USGS Water Services
class USGSWaterAPI {
constructor() {
this.baseUrl = "https://waterservices.usgs.gov/nwis/iv";
}
async getSiteData(siteNumber, parameterCode = "00060") {
const params = new URLSearchParams({
format: "json",
sites: siteNumber,
parameterCd: parameterCode,
period: "P7D", // Last 7 days
});
const response = await fetch(`${this.baseUrl}?${params}`);
return response.json();
}
async getSitesByState(state, parameterCode = "00060") {
const params = new URLSearchParams({
format: "json",
stateCd: state,
parameterCd: parameterCode,
siteStatus: "active",
});
const response = await fetch(`${this.baseUrl}?${params}`);
return response.json();
}
}
NASA APIs#
class NASAAPI {
constructor(apiKey) {
this.apiKey = apiKey;
}
// NASA Earth Imagery API
async getEarthImagery(lat, lon, date, options = {}) {
const params = new URLSearchParams({
lat: lat,
lon: lon,
date: date,
api_key: this.apiKey,
...options,
});
const response = await fetch(
`https://api.nasa.gov/planetary/earth/imagery?${params}`
);
return response.blob();
}
// NASA FIRMS (Fire Information for Resource Management System)
async getActiveFires(source = "VIIRS_SNPP_NRT", days = 1) {
const url = `https://firms.modaps.eosdis.nasa.gov/data/active_fire/${source}/csv/DL_FIRE_${source}_${days}.csv`;
const response = await fetch(url);
const csvText = await response.text();
return this.parseFireCSV(csvText);
}
parseFireCSV(csvText) {
const lines = csvText.trim().split("\n");
const headers = lines[0].split(",");
const features = [];
for (let i = 1; i < lines.length; i++) {
const values = lines[i].split(",");
const properties = {};
headers.forEach((header, index) => {
properties[header] = values[index];
});
if (properties.latitude && properties.longitude) {
features.push({
type: "Feature",
geometry: {
type: "Point",
coordinates: [
parseFloat(properties.longitude),
parseFloat(properties.latitude),
],
},
properties: properties,
});
}
}
return {
type: "FeatureCollection",
features: features,
};
}
}
API Integration Patterns#
Unified API Client#
class UnifiedGeoAPI {
constructor(config) {
this.services = {};
// Initialize configured services
if (config.mapbox) {
this.services.mapbox = new MapboxAPI(config.mapbox.token);
}
if (config.google) {
this.services.google = new GoogleMapsAPI(config.google.apiKey);
}
if (config.nominatim) {
this.services.nominatim = new NominatimAPI();
}
}
// Unified geocoding with fallback
async geocode(address, options = {}) {
const preferredService = options.service || "nominatim";
const services = [preferredService, ...Object.keys(this.services)];
for (const serviceName of services) {
try {
const service = this.services[serviceName];
if (service && service.geocode) {
const result = await service.geocode(address);
return { service: serviceName, data: result };
}
} catch (error) {
console.warn(`Geocoding failed with ${serviceName}:`, error);
}
}
throw new Error("All geocoding services failed");
}
// Unified routing with fallback
async getRoute(origin, destination, options = {}) {
const preferredService = options.service || "mapbox";
try {
const service = this.services[preferredService];
if (service && service.getDirections) {
return await service.getDirections([origin, destination]);
}
} catch (error) {
console.warn(`Routing failed with ${preferredService}:`, error);
// Try alternative service
for (const [name, service] of Object.entries(this.services)) {
if (name !== preferredService && service.getDirections) {
try {
return await service.getDirections([origin, destination]);
} catch (err) {
console.warn(`Fallback routing failed with ${name}:`, err);
}
}
}
}
throw new Error("All routing services failed");
}
}
// Usage
const geoAPI = new UnifiedGeoAPI({
mapbox: { token: "your-mapbox-token" },
google: { apiKey: "your-google-key" },
nominatim: true,
});
// Will try Nominatim first, fallback to others if needed
const geocodeResult = await geoAPI.geocode("San Francisco, CA");
// Will try Mapbox first, fallback to Google if needed
const route = await geoAPI.getRoute([-122.4194, 37.7749], [-122.0839, 37.422]);
Rate Limiting and Caching#
class RateLimitedAPI {
constructor(apiInstance, requestsPerSecond = 1) {
this.api = apiInstance;
this.requestQueue = [];
this.lastRequestTime = 0;
this.minInterval = 1000 / requestsPerSecond;
this.cache = new Map();
this.cacheExpiry = 5 * 60 * 1000; // 5 minutes
}
async request(method, ...args) {
const cacheKey = JSON.stringify({ method, args });
// Check cache first
const cached = this.cache.get(cacheKey);
if (cached && Date.now() - cached.timestamp < this.cacheExpiry) {
return cached.data;
}
// Rate limiting
return new Promise((resolve, reject) => {
this.requestQueue.push({ method, args, resolve, reject });
this.processQueue();
});
}
async processQueue() {
if (this.requestQueue.length === 0) return;
const now = Date.now();
const timeSinceLastRequest = now - this.lastRequestTime;
if (timeSinceLastRequest >= this.minInterval) {
const { method, args, resolve, reject } = this.requestQueue.shift();
this.lastRequestTime = now;
try {
const result = await this.api[method](...args);
// Cache result
const cacheKey = JSON.stringify({ method, args });
this.cache.set(cacheKey, {
data: result,
timestamp: now,
});
resolve(result);
} catch (error) {
reject(error);
}
// Process next request
if (this.requestQueue.length > 0) {
setTimeout(() => this.processQueue(), this.minInterval);
}
} else {
// Wait and try again
setTimeout(
() => this.processQueue(),
this.minInterval - timeSinceLastRequest
);
}
}
clearCache() {
this.cache.clear();
}
}
// Usage
const rateLimitedNominatim = new RateLimitedAPI(new NominatimAPI(), 1); // 1 request per second
// These will be queued and executed with rate limiting
const result1 = await rateLimitedNominatim.request("search", "New York");
const result2 = await rateLimitedNominatim.request("search", "Los Angeles");
This comprehensive API reference provides the foundation for integrating various geospatial services into Web GIS applications. The examples demonstrate authentication, error handling, rate limiting, and data transformation patterns essential for production applications.