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
|
module OverpassPois
|
||||||
class FetchError < StandardError; end
|
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
|
# Each entry: an Overpass tag filter -> { label:, icon:, color: }, used
|
||||||
# both to build the query (unioning every filter) and to label/style
|
# 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).
|
# bbox: [south, west, north, east] (Overpass's own axis order).
|
||||||
def self.fetch(bbox, timeout: 60)
|
def self.fetch(bbox, timeout: 60)
|
||||||
query = build_query(bbox, timeout: timeout)
|
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
|
# overpass-api.de answers Net::HTTP's default "Ruby" User-Agent with
|
||||||
# a 406 (verified 2026-10-09) — any identifying UA gets through.
|
# 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 = Net::HTTP::Post.new(uri, "User-Agent" => "maptool (Overpass camper POIs plugin)")
|
||||||
request.set_form_data("data" => query)
|
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)
|
raise FetchError, "unexpected response #{response.code}" unless response.is_a?(Net::HTTPSuccess)
|
||||||
|
|
||||||
JSON.parse(response.body)
|
JSON.parse(response.body)
|
||||||
|
|||||||
@@ -65,8 +65,13 @@ module Maptool
|
|||||||
south, west, north, east = bbox
|
south, west, north, east = bbox
|
||||||
args = ["--south=#{south}", "--west=#{west}", "--north=#{north}", "--east=#{east}"]
|
args = ["--south=#{south}", "--west=#{west}", "--north=#{north}", "--east=#{east}"]
|
||||||
|
|
||||||
stdout, _stderr, status = Open3.capture3(command, *args, chdir: plugin_dir)
|
stdout, stderr, status = Open3.capture3(command, *args, chdir: plugin_dir)
|
||||||
raise RunError, "plugin exited with status #{status.exitstatus}" unless status.success?
|
# 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)
|
geojson = JSON.parse(stdout)
|
||||||
raise RunError, "plugin did not print a GeoJSON FeatureCollection" unless geojson.is_a?(Hash) && geojson["type"] == "FeatureCollection"
|
raise RunError, "plugin did not print a GeoJSON FeatureCollection" unless geojson.is_a?(Hash) && geojson["type"] == "FeatureCollection"
|
||||||
|
|||||||
Reference in new issue
Block a user