uMap - Debuggen

Moin!

wie man einigen Posts entnehmen kann bin ich am Basteln.

Eine Karte zeigt manchmal alle Icons an und dann einmal wieder nicht. Stattdessen wird der rote Balken für “fehlerhafte Serverantwort” eingeblendet.

Mit einem Click auf das Kreuz wird diese auch nicht ausgeblendet.

Kann man irgendwo das ganze debuggen um Fehler zu finden?

Gruß Jan

Hier noch der Link: http://umap.openstreetmap.fr/de/map/jan-seine-bahnkarte_490448 - im Bereich des Lübecker Bahnhofs sind einige dieser Icons.

Im Firefox mit F12 die “Web-Konsole” einschalten (fall das nur bei mir so ist, auch übers Menü “Extras” → “Web-Entwickler” → “Web-Konsole” zu erreichen), dort bei “Netzwerkanalyse” “Alles” auswählen und fröhlich in der Karte rumzoomen. Bei mir erscheint dann gelegentlich die Meldung “fehlerhafte Serverantwort” in umap und unten in der Konsole finde ich Einträge:

"GET https://overpass-api.de/api/interpreter?data=[out:json][timeout:25];way["abandoned:railway"="signal_box"](53.82264508177111,10.594940185546875,53.8630560188767,10.795955657958986);out center;"

Status 429 Too Many Requests

Ich vermute, das sind die fehlerhaften Serverantworten. Falls nicht, findet man in dieser Konsole sicher noch viele andere Dinge…

Grüße
Max

Hallo Max,

diese Meldung hatte ich auch.

Aber wenn das schon bei der keinen Karte kommt, dann steckt da nicht viel Power hinter.

Wie wird es dann erst bei “richtigen” Karten.

Bei einer anderen Karte sind viel mehr Daten und da gibt es diese Meldung nicht so schnell.

http://umap.openstreetmap.fr/de/map/hundekotbeutel-spender_490433

Gruß Jan

Ich hab keine Ahnung, wann overpass findet, dass man zu aufdringlich ist. Ein Unterschied der beiden Karten ist, dass Du bei der “grossen” Karte nur eine Verbindung dorthin aufmachst. Bei der “kleinen” Karte kommen die Abfragen im Dreierpack für railway=signal_box, abandoned:railway=signal_box und disused:railway=signal_box. Die Ablehnung betrifft immer nur eine oder zwei davon. Es geht wohl overpass weniger um die Datenmenge, sondern mehr um die Anzahl der Abfragen.

probier mal die overpass api auf dem UMAP-server: api.openstreetmap.fr/api/interpreter (hab ich auch grad entdeckt)

Da muss man nicht viel irgendwo entdecken, einfach im Wiki nachzulesen:
https://wiki.openstreetmap.org/wiki/Overpass_API#Public_Overpass_API_instances

Ja, meiner Erfahrung nach, ist es auch eher die Häufigkeit von Abfragen. Teils habe ich schon 429er, wenn ich auf openstreetmap.org via Objektabfrage zu oft hintereinander mache :roll_eyes:
Daher denke ich auch, dass du halt 4 Ebenen mit jeweils einer Overpass-Abfrage hast, wird das Problem sein. Besser wäre halt in einer Abfrage das Gebiet nur 1x abzufragen nach allen 4 Ebenen-Objekte gleichzeitig. Weiß jetzt aber nicht, wie man dies dann als einzelne Ebenen in uMap dann händelt. Wenn das mit uMap nicht geht, wäre mein erster Einfall beispielsweise via PHP ein Caching-Script zuschreiben und das auf einen Webspace zu packen. Das PHP-Script fragt selbst nur 1x bei Overpass die Daten ab (für alle Ebenen) und uMap kann je Ebene abfragen und das kurzzeitig gecachte Ergebnis entsprechend Ebene vom PHP-Script filtern lassen. Dann sollte das Problem stark eingedämmt sein.

Wenn da bezüglich der Idee Fragezeichen sind, gern einfach fragen.

Gruß,
asca

Danke für den Tipp. Auf der deutschen Seite ist die Tabelle nicht vorhanden. (Man hätte natürlich auch den Hinweis auf noch nicht übersetzte Kapitel sehen können :sunglasses: )

Leider kann man kein eigenes PHP in UMAP verwenden.

Hä? Natürlich? Statt halt in uMap 4x URLs zu https://overpass-api.de/api/interpreter?data=[out:json]… einzutragen, kannst du doch genauso 4x URLs eintragen zu https://dein-eigener-server.de/overpass-api-proxy.php?..

Und das PHP-Script overpass-api-proxy.php bündelt halt die zigfachen Anfragen, indem es beim ersten Aufruf gleich alle 4 Anfragen an https://overpass-api.de/api/interpreter in einem stellt und dann jeweils das ausgibt, was die Anfrage an overpass-api-proxy.php gerade exakt anforderte. Die 2-3. Anfrage an overpass-api-proxy.php wird dann aus dem Cache beantwortet. Somit kommt bei https://overpass-api.de/api/interpreter nur eine Anfrage bei raus.

Wenn ich nachher mal ein bisschen Zeit habe, kann ich mal ein Beispiel schreiben/aufsetzen.

Also ich hab’s dann mal auf meinem Bastelserver umgesetzt und funktioniert wie man hier sehen kann:
https://umap.openstreetmap.de/de/map/unbenannte-karte_14376#12/53.8902/10.6725
Da auf Bastelserver kein Zertifkat für die verwendete Domain existiert, zuvor einmal irgendeine Abfrage wie diese öffnen und dann bestätigen, dass man eine Ausnahme hinzufügt. Danach sollte es auch in uMap dann von dort ohne Meckermeldung laden können.

Wie man dann in uMap sieht, wird statt von Overpass 4x je BBox-Bewegung die Daten abzufragen, nun von https://a404.de/dnd/osm/multioverpass/index.php 4x Daten abgefragt. Hierbei enthält allerdings nicht wie zuvor jede Abfrage-URL auch nur eine Overpass-Abfrage, sondern sie enthält gleich alle Overpass-Abfragen, welche in dem Bereich passieren sollen.
Somit ist es möglich, dass der erste Aufruf vom “multioverpass”-Script halt gebündelt einmalig bei Overpass alles für die gegebene BBox abfragt und dann die Antwort zwischenspeichert. Alle drei weiteren Aufrufe von “multioverpass” (ich nenn das jetzt MOP) seitens uMap, werden dann aus dem Zwischenspeicher beantwortet.

Die Anfrage an MOP besteht aus mehreren Parametern:

  • target - gibt an, welche Antworten aus der Overpass-Anfrage man jetzt spezifisch haben möchte. Man hat ja in dem Falle 4 gleichzeitig gestellt und hiermit wählt man aus, welche man nun als Daten zurückgeliefert bekommt haben möchte. Dies ist der einzige Wert, welcher sich dann jeweils in den URL-Angaben in uMap ändert.

  • timeout - gibt das Timeout für die Overpass-Anfrage an (also für alle kombinierten)

  • cache - gibt an in Sekunden, ab wann eine Cache-Datei als veraltet gilt und neu abgefragt wird

  • bbox - gibt die BBox an

  • alle weiteren Parameter werden dann als Overpass-Anfragen verarbeitet. D.h. ein weiterer Parameter, dessen Name kann in “target” angegeben werden und der Wert wird an Overpass weitergeleitet

Beispiel:
In uMap kann man nun eintragen:

https://a404.de/dnd/osm/multioverpass/?target=signal_box
&timeout=60
&cache=30
&bbox={south},{west},{north},{east}
&disused_signal_box=nw["disused:railway"="signal_box"]({{bbox}});out center;
&signal_box=nw["railway"="signal_box"]({{bbox}});out center;
&water_tower=(nw["railway"="water_tower"]({{bbox}});nw["man_made"="water_tower"]({{bbox}});nw["disused:railway"="water_tower"]({{bbox}}););out center;
&abandoned_signal_box=nw["abandoned:railway"="signal_box"]({{bbox}});out center;

Wie man sieht, sind hier 4 weitere Parameter/Overpass-Anfragen enthalten (disused_signal_box, signal_box, water_tower und abandoned_signal_box) und in target wird angegeben, dass man allerdings nur die Werte für signal_box haben möchte. An Overpass wird aber alles 4 zusammen als eine Anfrage gestellt und die eine Rückantwort mit Ergebnissen für alle 4 Unterabfragen im Cache gespeichert.
Die nächste uMap-Ebene hat dann

https://a404.de/dnd/osm/multioverpass/?target=water_tower&timeout=60&cache=30&...

und so weiter.

BBox:
Ein weitere Vorteil beim Entwickeln ist nun:
Overpass-Turbo und uMap haben beide eine eingebaute Möglichkeit, die bbox zu setzen. In Overpass-Turbo via {{bbox}} und in uMap wie ich feststellen konnte auch via {bbox}. Allerdings unterscheidet es sich einmal daran, dass uMap nur einmal geschweifte Klammern verwendet und in der Reihenfolge der Werte (uMap = west,south,east,north vs. Overpass = south,west,north,east).
Ich händel das ganze jetzt ebenfalls via Script und man kann weiterhin in den Abfragen welche in uMap in der URL stehen {{bbox}} verwenden, obwohl dies dann eigentlich zerschossen wird zu “{10.123,53.123,11.123,53.001}” womit Overpass nichts anfangen kann, da es “53.123,10.123,53.001,11.123” erwarten würde. Aber mein Script friemelt da wieder gerade, weshalb es (u.a.) den Parameter bbox gibt. Man kann also eine Unterabfrage aus uMap 1:1 in Overpass-Turbo kopieren und schauen/bearbeiten.

multioverpass-Abschnitt in JSON-Antwort:
Achja und in der JSON-Antwort gibt es jetzt angereichert noch einen Abschnitt “multioverpass”, der wie folgt aussieht:


	"multioverpass": {
		"request_time": "2021-05-31T05:42:51+02:00",
		"overpassURI": "https://overpass-api.de/api/interpreter?data=[out:json][timeout:60];make seperator \"name\" = \"signal_box\";out;nw[\"railway\"=\"signal_box\"](53.7892944367402,10.531425476074219,53.97931054041794,10.75492858886719);out center;",
		"source": "overpass"
	}

Hier kann man einerseits dann die URI sehen, welche letztlich an Overpass geschickt wurde. Zudem den Zeitpunkt von wann die Daten stammen. In “source” steht entweder “overpass” (dann wurde in diesem MOP-Request eben auch der Overpass-Request abgesendet) oder halt “cache”, dann kommen die Daten aus der Cache-Datei.
Das sieht man dann auch in der oberigen uMap mit den 4 Ebenen. 1x hat man dann im source “overpass” zu stehen und 3x halt “cache”.

ToDo:

  • Fehlerbehandlungen (Overpass antwortet nicht, etc.)

  • bbox - Verarbeitung ordentlich machen

  • direktes Löschen zu alter Caches

  • Typofehler in der URL “mulitoverpass” den ich gerade sehe -.- fixed

  • Tests

  • ggf. komplettes Redesign und saubereres aufsetzen. Aktuell hatte ich den Fokus auf Funktionsdemonstration und “alles in eine Script-Datei”.
    Könnte mir vorstellen, dass es durchaus allgemein recht sinnvoll sein könnte, denn es entlastet die Overpass-API-Server und hatte ich bislang auch selbst vermisst.

Code:
Hier nun der komplette Code. Hab ein paar Kommentare zum Verständnis eingefügt, aber bei Fragen gern fragen.


<?
define(KEY_PARAMS, array('bbox', 'timeout', 'target', 'cache'));

$cacheDir = 'cache/';
if(!is_dir($cacheDir)) mkdir($cacheDir);
$filename = $cacheDir . md5($_REQUEST['bbox']) . ".json";

$f = fopen($filename, 'x');
if($f !== FALSE) { // Cachefile existiert nicht, API muss abgefragt werden
  flock($f, LOCK_EX); // Lock auf leeres Cachefile setzen
  
  // Overpass-Abfrage-URI zusammenbauen:
  $timeout = (empty($_REQUEST['timeout'])) ? '60' : $_REQUEST['timeout'];
  $fullRequest = '[out:json][timeout:'.$timeout.'];';
  foreach($_REQUEST as $key => $value) {
    if(!in_array($key, KEY_PARAMS)) {
      $fullRequest .= 'make seperator "name" = "'.$key.'";out;';
      $fullRequest .= $value;
    }
  }
  $fullRequest = str_replace(' ', '%20', $fullRequest);
  $fullRequest = solveStupidWrongUmapBBox($fullRequest, $_REQUEST['bbox']);
  $overpassURI = 'https://overpass-api.de/api/interpreter?data='.$fullRequest;
  
  // Overpass einmalig abfragen:
  $overpassAnswer = file_get_contents($overpassURI);
  if($overpassAnswer === FALSE) {
    // Overpass-Anfrage fehlerhaft:
    header('Content-Type: application/json');
    $answer = array();
    $answer['multioverpass'] = array();
    $answer['error'] = "overpass-request failed, request URI: ".$overpassURI;
    echo json_encode($answer);
    flock($f, LOCK_UN);
    fclose($f);
    unlink($filename);
  } else {
    // Anreichern der Antwort mit Abschnitt multioverpass/request_time:
    $overpassAnswerJSON = json_decode($overpassAnswer, true);
    $overpassAnswerJSON['multioverpass'] = array();
    $overpassAnswerJSON['multioverpass']['request_time'] = date('c');
    $overpassAnswerJSON['multioverpass']['overpassURI'] = $overpassURI;
    
    // Overpass-Antwort in Cache schreiben:
    ftruncate($f, 0);
    fwrite($f, json_encode($overpassAnswerJSON));
    fflush($f);
    flock($f, LOCK_UN); // Lock entfernen
    fclose($f);

    // spezifische Antwort extrahieren:
    handleAnswer($overpassAnswerJSON, $_REQUEST['target']);
  }
} else { // Cache vorhanden, Daten aus Cache auslesen:
  $f = fopen($filename, 'r');
  if(flock($f, LOCK_SH)){ // Warten auf Ende vom Schreiblock
    // Auslesen gecachte Antwort:
    $cachedAnswer = fread($f, filesize($filename));
    fclose($f);
    
    $cacheTimeout = (empty($_REQUEST['cache'])) ? '300' : $_REQUEST['cache'];
    // spezifische Antwort extrahieren:
    handleAnswer(json_decode($cachedAnswer, true), $_REQUEST['target'], $cacheTimeout);
  }
  fclose($f);
}

function handleAnswer($answerJSON, $target, $cacheTimeout = null) {
  global $filename;
  
  header('Access-Control-Allow-Origin: *');
  header('Access-Control-Max-Age: 600');
  header('Content-Type: application/json');
  
  // spezifische Antwort in $jsonResult:
  $jsonResult = array();
  
  // alles schonmal außer 'elements' übertragen:
  foreach($answerJSON as $key => $value) {
    if($key != 'elements') {
      $jsonResult[$key] = $value;
    }
  }
  
  if($cacheTimeout !== null) { // Overpass-Antwort stammt aus Cache:
    // prüfen ob Cache zu alt ist:
    $request_time = strtotime($answerJSON['multioverpass']['request_time']);
    if(time() - $request_time > $cacheTimeout) { // Cache zu alt, Cache-Datei löschen:
      if(unlink($filename) !== FALSE) {
        // Sender auffordern, erneut Anfrage auszulösen:
        header('Location: '.$_SERVER['REQUEST_URI'], true, 303);
        die();
      } else {
        $jsonResult['multioverpass']['warning'] = 'could not delete cache file, see request_time';
      }
    }
    $jsonResult['multioverpass']['source'] = 'cache';
    
  } else { // Overpass-Antwort stammt nicht aus Cache:
    $jsonResult['multioverpass']['source'] = 'overpass';
  }
  
  // spezifisch angefragte Elemente aus 'elements' extrahieren:
  $targetElements = array();
  $foundTarget = false;
  for($elementsPos = 0; $elementsPos < count($answerJSON['elements']); $elementsPos++) {
    $element = $answerJSON['elements'][$elementsPos];
    
    if($element['type'] == 'seperator') { // Seperator-Element gefunden:
      if($foundTarget) { // Wenn foundTarget schon true ist, ist dies der 2. Seperator, somit Ende der angeforderten Elemente:
        break; // Abbruch des Durchsuchens
      }
      if($element['tags']['name'] == $target) { // Seperator hat Name des gesuchten Abschnitts:
        $foundTarget = true;
      }
    } else {      
      if($foundTarget) { // gesuchte Elemente übertragen
        $targetElements[] = $element;
      }
    }
  }
  $jsonResult['elements'] = $targetElements;
  echo json_encode($jsonResult);
}

// BBox-Werte sind unterschiedlich, daher... hmpf...
// {bbox} in uMap = west,south,east,north
// {{bbox}} in Overpass = south,west,north,east
function solveStupidWrongUmapBBox($uri, $bbox) {
  $bboxParts = explode(',', $bbox);
  $uMapBox = '{'.$bboxParts[1].','.$bboxParts[0].','.$bboxParts[3].','.$bboxParts[2].'}';
  return str_replace($uMapBox, $bbox, $uri);
}

Gruß,
asca

PS: Gern kann für Eigenbedarf gern mein aktuell gehostetes MOP genutzt werden. Ich garantiere aber natürlich für nichts, auch wenn ich eigentlich es nicht löschen sollte (“dnd” steht in der URL für “do not delete” als Hinweis für mich selbst), aber es tut mir sicherlich nicht weh, wenn das jetzt jemand für seine private uMap-Karte mit zig Overpass-Abfrage-URLs benutzt.

Ich fürchte, das wird vom Ansatz her nicht gut funktionieren, sobald mehrere Leute (auch für unterschiedliche Karten) diesen Proxy benutzen und alle Anfragen an den Overpass Server von derselben IP-Adresse deines Servers geschickt werden. Das Rate-Limiting wird da sehr schnell den Zugang für alle Karten blockieren.

Caching als Strategie dürfte auch nur für den Fall wirklich effektiv sein, wenn jemand den gesamten Planet 1x am Tag durchnudelt, und das Ergebnis für 24h speichert. Wirklich gut ist das auch nicht.

Im Moment sehe ich folgende Optionen:

  • Logik zum Zusammenfassen von Queries in umap selbst implementieren, also in Javascript, so dass das direkt im Browser läuft
  • Overpass Server verbessern (bessere Performance mit kleineren Queries, weniger Last auf dem Server, etc.)

Das hab ich auch nie behauptet, dass das passieren soll. Und es funktioniert bereits jetzt deutlich besser als zigfach Overpass-API in uMap eingetragen zu haben. Wie man leicht auf der Map vom Themenersteller sehen kann, hagelt es 429-Meldungen von Overpass. Das passiert nun nicht mehr.
Das Script habe ich so geschrieben (ohne Datenbank) veröffentlicht, damit jeder das für sich super simpel auf einen 08/15-Webhost packen kann. Das ist der spezifische Zweck.

Ja finde hier hat uMap selbst stark Verbesserungspotenzial an sich.
BBox wird minimalst verschoben/verändert → alles wird erneut abgefragen.
Es wird reingezoomt → alles wird erneut abgefragt (wieso? Die BBox wird kleiner und ist im Bereich der alten).
Es wird zurückgezoomt/verschoben → erneute Datenabfrage, hier könnte man schonmal minimales Caching haben
Diesbezüglich wäre es schonmal ungemein sinnvoller, wenn hier uMap bereits einerseits feststellt, was unnötige Requests sind, hier kann schnell drastisch reduziert werden.

Ideal sicherlich auch ein Zusammenfassen, wobei dann halt die uMap-Option “ausgelagerte Daten via dynamischer URI” halt Overpass-URIs erkennen und berücksichtigen müsste. Hatte auch erst überlegt, ob ich es als JS-Erweiterung für Tamper-/Grease-Monkey baue, welche dann uMap erweitert. Fand die Lösung so jetzt halt nur allgemeingültiger (nicht nur für uMap nutzbar) und auch schneller/einfacher nutzbar. Zudem funktioniert es für den uMap-Betrachter transparent. Wenn ich mir also eine Karte erstelle und dann auf einem anderen PC es anschaue, geht’s immernoch, auch wenn ich die JS-Erweiterung nicht drin habe.

Aber ja, denkbar ist es absolut, dass hier uMap erkennt, dass zigfach Overpass abgefragt werden soll und dann entsprechend alle Einzelabfragen zusammenfässt und nur einmal abfragt. Aktuell werkelt hier halt jeder Layer/Ebene in uMap für sich. Da müsste man sich jetzt erstmal arg den Kopf zerbrechen, wie man es genau haben möchte.

Gut, das kann man natürlich immer und wenn man jetzt simpel das Umsetzen könnte, was du schreibst, hätte man es ja sicherlich bereits getan :wink:
Denkbar wäre allerdings, dass auch Overpass-API sowas wie ich geschrieben habe selbst mit anbietet. Dann hätte sich dein angesprochenes Problem mit Rate-Limiting ja erledigt. Im Prinzip haben die Overpass-API-Server ja selbst ein Interesse daran, eine Möglichkeit anzubieten, dass nicht zig Dinge einzeln abgefragt werden, was auch problemfrei zusammengefasst werden könnte. Ich würd mal gern wissen, wieviel zusätzliche Last z.B. auf https://overpass-api.de/ allein dadurch entsteht, weil auf den Standardkarten openstreetmap.org/.de via Objektabfrage immer gleich zwei einzelne Abfragen an https://overpass-api.de/ rausgehen. Wobei natürlich auch hier es clientseitig gelöst werden könnte indem halt das JS von openstreetmap.org/de bei einer Objektanfrage nur eine Abfrage stellt wo beides zusammen abgefragt wird.

Und ich rede jetzt explizit nicht davon, dass jetzt jede Overpass-API-Anfrage bzw. deren Ergebnis gecacht wird (das würde vieles auch kaputt machen), sondern dass lediglich die Overpass-API selbst eine Erweiterung hat, mit der es möglich ist (bei Bedarf und expliziter Angabe), dass mehrere einzelne HTTP-Anfragen faktisch nur einmal die Overpass-API veranlassen “zu arbeiten”.

Letztlich sollte sich aber dennoch immer der Client einer Overpass-API immer Gedanken dazu machen, wie er möglichst nicht zahlreiche HTTP-Anfragen unnötig an die API hinrotzt. Und auch der Client sollte sauber https://overpass-api.de/api/kill_my_queries aufrufen, wenn die Antworten von Overpass überhaupt nicht mehr benötigt werden. Das sollte uMap z.B. nämlich auch berücksichtigen (dafür müsste aber wiederum wie gesagt uMap natürlich die dynamische URL interpretieren und verstehen “ah, das ist Overpass, hier musss ich x und y tun”).
Da “kill_my_queries” ist mir vorhin erst noch in Sinn gekommen, weil ich es bei Overpass-Turbo sah. Will ich jetzt auch noch in mein Script einbauen, dass wenn der selbe Abfragende eine neue Anfrage mit anderer BBox stellt, dass dann die alten Anfragen abgebrochen werden.

Ich dachte, ich weise für den Punkt “Gern kann für Eigenbedarf gern mein aktuell gehostetes MOP genutzt werden” nochmal darauf hin, dass hier auch Probleme mit dem Rate Limiting lauern können.

Zustimmung, da ist auf jeden Fall noch einiges an Potenzial und es würde vielen umap-Nutzern weiterhelfen, die vielleicht nicht so ohne weiteres eigenen Code hosten können oder möchten.

Die Antwort dazu würde leider den Rahmen hier etwas sprengen. Vielleicht soviel, sehr viele kleine Abfragen sind technisch auf jeden Fall machbar, auch mit Query-Laufzeiten <10ms. Allerdings ist die Umsetzung in Richtung Produktion noch nicht weit genug fortgeschritten. Momentan ist es einfach noch so, dass die API besser mit weniger, aber etwas größeren Queries arbeitet. Queries zusammenfassen hilft also auf jeden Fall schon mal weiter für den Moment.

Bei über 1 Mio Abfragen pro Tag fällt die Objektabfrage nicht so sehr ins Gewicht, dafür wird das relativ gesehen einfach zu wenig genutzt.

Gerade mal ein wenig unter die uMap-Haube geschaut, um einzuschätzen ob ich da selbst Code-Änderungen machen könnte/vorschlagen und dabei diesen Issue von vorgestern gesehen: https://github.com/umap-project/umap/issues/914

Ich sehe gerade dass schoka dieses fast 1 Jahr alte Thema ausgegraben hat (was dann auch erklärt, wieso Lübeck als Themenersteller gar nicht aktiv sich dazu bislang äußerte).
Naja, aber es zeigt, dass es somit ein andauerndes Problem mit uMap + Overpass ist.

Gruß,
asca

Wahrscheinlich fehlt es auch bei UMAP an engagierten Freiwilligen. Von mir würdest du einen Pluspunkt bekommen, wenn du deinen Vorschlag dort einbringst :slight_smile: .

Hier mal als Demo, wie so etwas aussehen könnte: https://umap.openstreetmap.fr/en/map/demo-dev-server_157804#14/52.4272/9.8416

Und was ist an https://dev.overpass-api.de/api_mmd/47ae7307-9f60-4d5a-8346-696c95057981/interpreter halt technisch anders, dass es dann mit “sehr vielen kleinen Abfragen” nun besser klarkommt?
Oder ist es halt einfach, dass dort die Timeouts/Limits einfach nur hochkonfiguriert sind, sodass es halt seltener/nicht dazu kommt und sorgt halt daher für keine Probleme, weil’s deutlich weniger genutzt wird, als https://overpass-api.de/interpreter ? Dann ist es ja nicht vergleichbar, denn würde man das auf https://overpass-api.de/interpreter so einstellen, wäre dieser dann wohl schneller komplett überlastet, was auch keine Lösung sein kann.