refactor: enhance Overpass API handling with multiple endpoints and improved error reporting
This commit is contained in:
1 parent
33be95ef59
commit
efddc55ad0
2 files changed
+29
-5
No files matched your search
@@ -12,7 +12,15 @@ module Maptool
|
||||
module OverpassPois
|
||||
class FetchError < StandardError; end
|
||||
|
||||
OVERPASS_API = "https://overpass-api.de/api/interpreter".freeze
|
||||
# Same three public mirrors public/js/hiking_trails.js races — tried in
|
||||
# turn here instead, falling through to the next on any failure: the
|
||||
# shared free instances regularly 504/429 under load, and a plugin run
|
||||
# is a one-off click, not worth threads to race them.
|
||||
OVERPASS_APIS = [
|
||||
"https://overpass-api.de/api/interpreter",
|
||||
"https://overpass.kumi.systems/api/interpreter",
|
||||
"https://overpass.private.coffee/api/interpreter",
|
||||
].freeze
|
||||
|
||||
# Each entry: an Overpass tag filter -> { label:, icon:, color: }, used
|
||||
# both to build the query (unioning every filter) and to label/style
|
||||
@@ -53,12 +61,23 @@ module Maptool
|
||||
# bbox: [south, west, north, east] (Overpass's own axis order).
|
||||
def self.fetch(bbox, timeout: 60)
|
||||
query = build_query(bbox, timeout: timeout)
|
||||
uri = URI(OVERPASS_API)
|
||||
errors = OVERPASS_APIS.map do |url|
|
||||
return post(URI(url), query, timeout)
|
||||
rescue FetchError, Net::OpenTimeout, Net::ReadTimeout, SystemCallError, SocketError, OpenSSL::SSL::SSLError, JSON::ParserError => e
|
||||
"#{URI(url).host}: #{e.message}"
|
||||
end
|
||||
raise FetchError, errors.join("; ")
|
||||
end
|
||||
|
||||
def self.post(uri, query, timeout)
|
||||
# overpass-api.de answers Net::HTTP's default "Ruby" User-Agent with
|
||||
# a 406 (verified 2026-10-09) — any identifying UA gets through.
|
||||
request = Net::HTTP::Post.new(uri, "User-Agent" => "maptool (Overpass camper POIs plugin)")
|
||||
request.set_form_data("data" => query)
|
||||
response = Net::HTTP.start(uri.host, uri.port, use_ssl: true) { |http| http.request(request) }
|
||||
# read_timeout past the query's own [timeout:] — Net::HTTP's default
|
||||
# 60s equals it, so a big viewport could hit Net::ReadTimeout just as
|
||||
# Overpass was about to answer (a country-sized bbox measured ~57s).
|
||||
response = Net::HTTP.start(uri.host, uri.port, use_ssl: true, read_timeout: timeout + 30) { |http| http.request(request) }
|
||||
raise FetchError, "unexpected response #{response.code}" unless response.is_a?(Net::HTTPSuccess)
|
||||
|
||||
JSON.parse(response.body)
|
||||
|
||||
@@ -65,8 +65,13 @@ module Maptool
|
||||
south, west, north, east = bbox
|
||||
args = ["--south=#{south}", "--west=#{west}", "--north=#{north}", "--east=#{east}"]
|
||||
|
||||
stdout, _stderr, status = Open3.capture3(command, *args, chdir: plugin_dir)
|
||||
raise RunError, "plugin exited with status #{status.exitstatus}" unless status.success?
|
||||
stdout, stderr, status = Open3.capture3(command, *args, chdir: plugin_dir)
|
||||
# First stderr line too — for a Ruby plugin that's the exception
|
||||
# message (e.g. "...: unexpected response 406"), the only clue to why.
|
||||
unless status.success?
|
||||
reason = stderr.lines.first&.strip
|
||||
raise RunError, ["plugin exited with status #{status.exitstatus}", reason].compact.reject(&:empty?).join(": ")
|
||||
end
|
||||
|
||||
geojson = JSON.parse(stdout)
|
||||
raise RunError, "plugin did not print a GeoJSON FeatureCollection" unless geojson.is_a?(Hash) && geojson["type"] == "FeatureCollection"
|
||||
|
||||
Reference in new issue
Block a user