diff --git a/assets/layers/cycle_highways/cycle_highways.json b/assets/layers/cycle_highways/cycle_highways.json index 1a6f908d1..dec199887 100644 --- a/assets/layers/cycle_highways/cycle_highways.json +++ b/assets/layers/cycle_highways/cycle_highways.json @@ -90,11 +90,11 @@ "minzoom": 6, "title": { "render": { - "en": "Cycle highway", + "en": "cycle highway", "de": "Radschnellweg", - "ca": "Via ciclista", + "ca": "via ciclista", "fr": "Aménagement cyclable", - "nl": "Fietssnelweg", + "nl": "fietssnelweg", "es": "autovía ciclista", "nb_NO": "sykkelmotorvei", "da": "cykelmotorvej", diff --git a/assets/layers/shops/shops.json b/assets/layers/shops/shops.json index f8f97cd11..af486d593 100644 --- a/assets/layers/shops/shops.json +++ b/assets/layers/shops/shops.json @@ -257,6 +257,9 @@ { "builtin": "id_presets.shop_types", "override": { + "labels": [ + "description" + ], "question": { "en": "What kind of shop is this?", "nl": "Wat voor soort winkel is dit?", diff --git a/assets/themes/cycle_highways/cycle_highways.json b/assets/themes/cycle_highways/cycle_highways.json index 6a0a73699..3d58409f2 100644 --- a/assets/themes/cycle_highways/cycle_highways.json +++ b/assets/themes/cycle_highways/cycle_highways.json @@ -74,7 +74,6 @@ ] } }, - "cycle_highways" ], "overpassTimeout": 60, diff --git a/langs/layers/de.json b/langs/layers/de.json index 8862289c6..3ffaf7776 100644 --- a/langs/layers/de.json +++ b/langs/layers/de.json @@ -35,23 +35,6 @@ "1": { "title": "eine freistehende Posterbox" }, - "10": { - "description": "Ein wasserfestes Textil mit einer aufgedruckten Botschaft, das dauerhaft an einer Wand verankert ist", - "title": "eine Plane" - }, - "11": { - "title": "ein Totem" - }, - "12": { - "description": "Verwendet für Werbeschilder, Leuchtreklamen, Logos und institutionelle Eingangsschilder", - "title": "ein Schild" - }, - "13": { - "title": "eine Skulptur" - }, - "14": { - "title": "eine Wandmalerei" - }, "2": { "title": "eine wandmontierte Posterbox" }, @@ -77,6 +60,23 @@ }, "9": { "title": "ein Bildschirm, der an einem Wartehäuschen angebracht ist" + }, + "10": { + "description": "Ein wasserfestes Textil mit einer aufgedruckten Botschaft, das dauerhaft an einer Wand verankert ist", + "title": "eine Plane" + }, + "11": { + "title": "ein Totem" + }, + "12": { + "description": "Verwendet für Werbeschilder, Leuchtreklamen, Logos und institutionelle Eingangsschilder", + "title": "ein Schild" + }, + "13": { + "title": "eine Skulptur" + }, + "14": { + "title": "eine Wandmalerei" } }, "tagRenderings": { @@ -171,9 +171,6 @@ "1": { "then": "Dies ist ein Brett" }, - "10": { - "then": "Dies ist eine Wandmalerei" - }, "2": { "then": "Dies ist eine Litfaßsäule" }, @@ -197,6 +194,9 @@ }, "9": { "then": "Dies ist ein Totem" + }, + "10": { + "then": "Dies ist eine Wandmalerei" } }, "question": "Welche Art von Werbung ist das?", @@ -211,9 +211,6 @@ "1": { "then": "Brett" }, - "10": { - "then": "Wandmalerei" - }, "2": { "then": "Posterbox" }, @@ -237,6 +234,9 @@ }, "9": { "then": "Totem" + }, + "10": { + "then": "Wandmalerei" } } } @@ -283,9 +283,6 @@ "1": { "then": "Es handelt sich um eine Seilbahn, bei der die Kabinen in ständigen Kreisen fahren" }, - "10": { - "then": "Eine Seilrutsche. (Eine Touristenattraktion, bei der abenteuerlustige Menschen mit hoher Geschwindigkeit hinunterfahren) " - }, "2": { "then": "Ein offener Sessellift mit Sitzgelegenheiten und Zugang zur Außenluft." }, @@ -309,6 +306,9 @@ }, "9": { "then": "Ein magic carpet (ein Förderband auf dem Boden)" + }, + "10": { + "then": "Eine Seilrutsche. (Eine Touristenattraktion, bei der abenteuerlustige Menschen mit hoher Geschwindigkeit hinunterfahren) " } }, "question": "Um welchen Seilbahntyp handelt es sich?" @@ -453,15 +453,6 @@ "1": { "then": "Wandbild" }, - "10": { - "then": "Azulejo (spanische dekorative Fliesenarbeit)" - }, - "11": { - "then": "Fliesenarbeit" - }, - "12": { - "then": "Holzschnitzerei" - }, "2": { "then": "Malerei" }, @@ -485,6 +476,15 @@ }, "9": { "then": "Relief" + }, + "10": { + "then": "Azulejo (spanische dekorative Fliesenarbeit)" + }, + "11": { + "then": "Fliesenarbeit" + }, + "12": { + "then": "Holzschnitzerei" } }, "question": "Um welche Art Kunstwerk handelt es sich?", @@ -2088,6 +2088,30 @@ "1": { "question": "Verfügt über einen
Schuko-Stecker ohne Erdungsstift (CEE7/4 Typ F)
" }, + "2": { + "question": "Verfügt über einen
europäischen Netzstecker mit Erdungsstift (CEE7/4 Typ E)
Anschluss" + }, + "3": { + "question": "Verfügt über einen
Chademo
Stecker" + }, + "4": { + "question": "Verfügt über einen
Typ 1 (J1772)
Stecker mit Kabel" + }, + "5": { + "question": "Verfügt über einen
Typ 1 (J1772)Stecker ohne Kabel
" + }, + "6": { + "question": "Verfügt über einen
Typ 1 CCS (Typ 1 Combo)
Stecker" + }, + "7": { + "question": "Verfügt über einen
Tesla Supercharger
Stecker" + }, + "8": { + "question": "Hat einen
Typ 2 (Mennekes)
Anschluss" + }, + "9": { + "question": "Hat einen
Typ 2 CCS (Mennekes)
Anschluss" + }, "10": { "question": "Hat einen
Typ 2 (Mennekes)
Anschluss mit Kabel" }, @@ -2118,35 +2142,11 @@ "19": { "question": "Hat ein
SEV 1011 T23 (Type J)
Anschluss" }, - "2": { - "question": "Verfügt über einen
europäischen Netzstecker mit Erdungsstift (CEE7/4 Typ E)
Anschluss" - }, "20": { "question": "Hat ein
AS3112 (Type I)
Anschluss" }, "21": { "question": "Hat ein
NEMA 5-20 (Type B)
Anschluss" - }, - "3": { - "question": "Verfügt über einen
Chademo
Stecker" - }, - "4": { - "question": "Verfügt über einen
Typ 1 (J1772)
Stecker mit Kabel" - }, - "5": { - "question": "Verfügt über einen
Typ 1 (J1772)Stecker ohne Kabel
" - }, - "6": { - "question": "Verfügt über einen
Typ 1 CCS (Typ 1 Combo)
Stecker" - }, - "7": { - "question": "Verfügt über einen
Tesla Supercharger
Stecker" - }, - "8": { - "question": "Hat einen
Typ 2 (Mennekes)
Anschluss" - }, - "9": { - "question": "Hat einen
Typ 2 CCS (Mennekes)
Anschluss" } } } @@ -2202,6 +2202,30 @@ "1": { "then": "Schuko-Stecker ohne Erdungsstift (CEE7/4 Typ F)" }, + "2": { + "then": "Europäischer Netzstecker mit Erdungsstift (CEE7/4 Typ E)" + }, + "3": { + "then": "Europäischer Netzstecker mit Erdungsstift (CEE7/4 Typ E)" + }, + "4": { + "then": "Chademo-Anschluss" + }, + "5": { + "then": "Chademo-Anschluss" + }, + "6": { + "then": "Typ 1 mit Kabel (J1772)" + }, + "7": { + "then": "Typ 1 mit Kabel (J1772)" + }, + "8": { + "then": "Typ 1 ohne Kabel (J1772)" + }, + "9": { + "then": " Typ 1 ohne Kabel (J1772)" + }, "10": { "then": "Typ 1 CCS (Typ 1 Combo)" }, @@ -2232,9 +2256,6 @@ "19": { "then": "Typ 2 mit Kabel (mennekes)" }, - "2": { - "then": "Europäischer Netzstecker mit Erdungsstift (CEE7/4 Typ E)" - }, "20": { "then": "Tesla Supercharger CCS (Typ 2 CSS von Tesla)" }, @@ -2265,9 +2286,6 @@ "29": { "then": " Bosch Active Connect mit 3 Pins und Kabel" }, - "3": { - "then": "Europäischer Netzstecker mit Erdungsstift (CEE7/4 Typ E)" - }, "30": { "then": "Bosch Active Connect mit 5 Pins und Kabel" }, @@ -2298,29 +2316,11 @@ "39": { "then": "AS3112 (Typ I)" }, - "4": { - "then": "Chademo-Anschluss" - }, "40": { "then": "NEMA 5-20 (Typ B)" }, "41": { "then": "NEMA 5-20 (Typ B)" - }, - "5": { - "then": "Chademo-Anschluss" - }, - "6": { - "then": "Typ 1 mit Kabel (J1772)" - }, - "7": { - "then": "Typ 1 mit Kabel (J1772)" - }, - "8": { - "then": "Typ 1 ohne Kabel (J1772)" - }, - "9": { - "then": " Typ 1 ohne Kabel (J1772)" } }, "question": "Welche Ladeanschlüsse gibt es hier?" @@ -2514,6 +2514,30 @@ "1": { "2": "Europäischer Netzstecker mit Erdungsstift (CEE7/4 Typ E)" }, + "2": { + "2": "Chademo-Stecker" + }, + "3": { + "2": "Typ 1 mit Kabel (J1772)" + }, + "4": { + "2": " Typ 1 ohne Kabel (J1772)" + }, + "5": { + "2": "Typ 1 CCS (Typ 1 Combo)" + }, + "6": { + "2": "Tesla Supercharger" + }, + "7": { + "2": "Typ 2 (Mennekes)" + }, + "8": { + "2": "Typ 2 CCS (Mennekes)" + }, + "9": { + "2": "Typ 2 mit Kabel (Mennekes)" + }, "10": { "2": "Tesla Supercharger CCS (Typ 2 CSS von Tesla)" }, @@ -2544,32 +2568,8 @@ "19": { "2": "AS3112 (Typ I)" }, - "2": { - "2": "Chademo-Stecker" - }, "20": { "2": "NEMA 5-20 (Typ B)" - }, - "3": { - "2": "Typ 1 mit Kabel (J1772)" - }, - "4": { - "2": " Typ 1 ohne Kabel (J1772)" - }, - "5": { - "2": "Typ 1 CCS (Typ 1 Combo)" - }, - "6": { - "2": "Tesla Supercharger" - }, - "7": { - "2": "Typ 2 (Mennekes)" - }, - "8": { - "2": "Typ 2 CCS (Mennekes)" - }, - "9": { - "2": "Typ 2 mit Kabel (Mennekes)" } } } @@ -3373,15 +3373,6 @@ "1": { "then": "Dieser Radweg hat einen festen Belag" }, - "10": { - "then": "Dieser Radweg besteht aus feinem Schotter" - }, - "11": { - "then": "Der Radweg ist aus Kies" - }, - "12": { - "then": "Dieser Radweg besteht aus Rohboden" - }, "2": { "then": "Der Radweg ist aus Asphalt" }, @@ -3405,6 +3396,15 @@ }, "9": { "then": "Der Radweg ist aus Schotter" + }, + "10": { + "then": "Dieser Radweg besteht aus feinem Schotter" + }, + "11": { + "then": "Der Radweg ist aus Kies" + }, + "12": { + "then": "Dieser Radweg besteht aus Rohboden" } }, "question": "Was ist der Belag dieses Radwegs?", @@ -3453,15 +3453,6 @@ "1": { "then": "Dieser Radweg hat einen festen Belag" }, - "10": { - "then": "Dieser Radweg besteht aus feinem Schotter" - }, - "11": { - "then": "Der Radweg ist aus Kies" - }, - "12": { - "then": "Dieser Radweg besteht aus Rohboden" - }, "2": { "then": "Der Radweg ist aus Asphalt" }, @@ -3485,6 +3476,15 @@ }, "9": { "then": "Der Radweg ist aus Schotter" + }, + "10": { + "then": "Dieser Radweg besteht aus feinem Schotter" + }, + "11": { + "then": "Der Radweg ist aus Kies" + }, + "12": { + "then": "Dieser Radweg besteht aus Rohboden" } }, "question": "Was ist der Belag dieser Straße?", @@ -4474,54 +4474,6 @@ } } }, - "10": { - "options": { - "0": { - "question": "Keine Bevorzugung von Hunden" - }, - "1": { - "question": "Hunde erlaubt" - }, - "2": { - "question": "Keine Hunde erlaubt" - } - } - }, - "11": { - "options": { - "0": { - "question": "Internetzugang vorhanden" - } - } - }, - "12": { - "options": { - "0": { - "question": "Stromanschluss vorhanden" - } - } - }, - "13": { - "options": { - "0": { - "question": "Hat zuckerfreie Angebote" - } - } - }, - "14": { - "options": { - "0": { - "question": "Hat glutenfreie Angebote" - } - } - }, - "15": { - "options": { - "0": { - "question": "Hat laktosefreie Angebote" - } - } - }, "2": { "options": { "0": { @@ -4592,6 +4544,54 @@ "question": "Nutzung kostenlos" } } + }, + "10": { + "options": { + "0": { + "question": "Keine Bevorzugung von Hunden" + }, + "1": { + "question": "Hunde erlaubt" + }, + "2": { + "question": "Keine Hunde erlaubt" + } + } + }, + "11": { + "options": { + "0": { + "question": "Internetzugang vorhanden" + } + } + }, + "12": { + "options": { + "0": { + "question": "Stromanschluss vorhanden" + } + } + }, + "13": { + "options": { + "0": { + "question": "Hat zuckerfreie Angebote" + } + } + }, + "14": { + "options": { + "0": { + "question": "Hat glutenfreie Angebote" + } + } + }, + "15": { + "options": { + "0": { + "question": "Hat laktosefreie Angebote" + } + } } } }, @@ -4754,6 +4754,30 @@ "1": { "then": "Die Fitness-Station hat ein Schild mit Anweisungen für eine bestimmte Übung." }, + "2": { + "then": "Die Fitness-Station hat eine Einrichtung für Sit-ups." + }, + "3": { + "then": "Die Fitness-Station hat eine Vorrichtung für Liegestütze. In der Regel eine oder mehrere niedrige Reckstangen." + }, + "4": { + "then": "Die Fitness-Station hat Stangen zum Dehnen." + }, + "5": { + "then": "Die Fitness-Station hat eine Vorrichtung für Rückenstrecker (Hyperextensions)." + }, + "6": { + "then": "Die Fitness-Station hat Ringe für Gymnastikübungen." + }, + "7": { + "then": "Die Fitness-Station hat eine horizontale Leiter (Monkey Bars)." + }, + "8": { + "then": "Die Fitness-Station hat eine Sprossenwand zum Klettern." + }, + "9": { + "then": "Die Fitness-Station hat Pfosten für Slalomübungen." + }, "10": { "then": "Die Fitness-Station hat Trittsteine." }, @@ -4784,9 +4808,6 @@ "19": { "then": "Die Fitness-Station hat Kampfseile (battle ropes)." }, - "2": { - "then": "Die Fitness-Station hat eine Einrichtung für Sit-ups." - }, "20": { "then": "Die Fitness-Station hat ein Fahrradergometer." }, @@ -4801,27 +4822,6 @@ }, "24": { "then": "Die Fitness-Station hat eine Slackline." - }, - "3": { - "then": "Die Fitness-Station hat eine Vorrichtung für Liegestütze. In der Regel eine oder mehrere niedrige Reckstangen." - }, - "4": { - "then": "Die Fitness-Station hat Stangen zum Dehnen." - }, - "5": { - "then": "Die Fitness-Station hat eine Vorrichtung für Rückenstrecker (Hyperextensions)." - }, - "6": { - "then": "Die Fitness-Station hat Ringe für Gymnastikübungen." - }, - "7": { - "then": "Die Fitness-Station hat eine horizontale Leiter (Monkey Bars)." - }, - "8": { - "then": "Die Fitness-Station hat eine Sprossenwand zum Klettern." - }, - "9": { - "then": "Die Fitness-Station hat Pfosten für Slalomübungen." } }, "question": "Welche Übungsgeräte gibt es an dieser Fitness-Station?" @@ -4941,21 +4941,6 @@ "1": { "then": "Dies ist eine Pommesbude" }, - "10": { - "then": "Hier werden chinesische Gerichte serviert" - }, - "11": { - "then": "Hier werden griechische Gerichte serviert" - }, - "12": { - "then": "Hier werden indische Gerichte serviert" - }, - "13": { - "then": "Hier werden türkische Gerichte serviert" - }, - "14": { - "then": "Hier werden thailändische Gerichte serviert" - }, "2": { "then": "Bietet vorwiegend Pastagerichte an" }, @@ -4979,6 +4964,21 @@ }, "9": { "then": "Hier werden französische Gerichte serviert" + }, + "10": { + "then": "Hier werden chinesische Gerichte serviert" + }, + "11": { + "then": "Hier werden griechische Gerichte serviert" + }, + "12": { + "then": "Hier werden indische Gerichte serviert" + }, + "13": { + "then": "Hier werden türkische Gerichte serviert" + }, + "14": { + "then": "Hier werden thailändische Gerichte serviert" } }, "question": "Was für Essen gibt es hier?", @@ -5729,6 +5729,30 @@ "1": { "then": "Dies ist ein Auditorium" }, + "2": { + "then": "Dies ist ein Schlafzimmer" + }, + "3": { + "then": "Dies ist eine Kapelle" + }, + "4": { + "then": "Dies ist ein Klassenzimmer" + }, + "5": { + "then": "Dies ist ein Klassenzimmer" + }, + "6": { + "then": "Dies ist ein Computerraum" + }, + "7": { + "then": "Dies ist ein Konferenzraum" + }, + "8": { + "then": "Dies ist eine Krypta" + }, + "9": { + "then": "Dies ist eine Küche" + }, "10": { "then": "Dies ist ein Labor" }, @@ -5759,9 +5783,6 @@ "19": { "then": "Dies ist ein Lagerraum" }, - "2": { - "then": "Dies ist ein Schlafzimmer" - }, "20": { "then": "Dies ist ein Technikraum" }, @@ -5770,27 +5791,6 @@ }, "22": { "then": "Dies ist ein Wartezimmer" - }, - "3": { - "then": "Dies ist eine Kapelle" - }, - "4": { - "then": "Dies ist ein Klassenzimmer" - }, - "5": { - "then": "Dies ist ein Klassenzimmer" - }, - "6": { - "then": "Dies ist ein Computerraum" - }, - "7": { - "then": "Dies ist ein Konferenzraum" - }, - "8": { - "then": "Dies ist eine Krypta" - }, - "9": { - "then": "Dies ist eine Küche" } }, "question": "Wie wird dieser Raum genutzt?" @@ -6341,21 +6341,6 @@ "1": { "then": "Dies ist eine Gedenktafel" }, - "10": { - "then": "Das ist ein Kreuz" - }, - "11": { - "then": "Dies ist eine blaue Plaque" - }, - "12": { - "then": "Dies ist ein historischer Panzer, der permanent in den öffentlichen Raum als Denkmal platziert wurde" - }, - "13": { - "then": "Das ist ein Baumdenkmal" - }, - "14": { - "then": "Dies ist ein Grabstein; die Person ist hier begraben" - }, "2": { "then": "Dies ist eine Gedenkbank" }, @@ -6379,6 +6364,21 @@ }, "9": { "then": "Das ist ein Obelisk" + }, + "10": { + "then": "Das ist ein Kreuz" + }, + "11": { + "then": "Dies ist eine blaue Plaque" + }, + "12": { + "then": "Dies ist ein historischer Panzer, der permanent in den öffentlichen Raum als Denkmal platziert wurde" + }, + "13": { + "then": "Das ist ein Baumdenkmal" + }, + "14": { + "then": "Dies ist ein Grabstein; die Person ist hier begraben" } }, "question": "Was für eine Art von Denkmal ist das?", @@ -6562,19 +6562,6 @@ } } }, - "10": { - "options": { - "0": { - "question": "Alle Notizen" - }, - "1": { - "question": "Importnotizen ausblenden" - }, - "2": { - "question": "Nur Importnotizen anzeigen" - } - } - }, "2": { "options": { "0": { @@ -6630,6 +6617,19 @@ "question": "Nur offene Notizen anzeigen" } } + }, + "10": { + "options": { + "0": { + "question": "Alle Notizen" + }, + "1": { + "question": "Importnotizen ausblenden" + }, + "2": { + "question": "Nur Importnotizen anzeigen" + } + } } }, "name": "OpenStreetMap-Hinweise", @@ -7008,18 +7008,6 @@ "1": { "then": "Dies ist ein normaler Stellplatz." }, - "10": { - "then": "Dies ist ein Stellplatz, der für das Personal reserviert ist." - }, - "11": { - "then": "Dies ist ein Stellplatz, der für Taxis reserviert ist." - }, - "12": { - "then": "Dies ist ein Stellplatz, der für Fahrzeuge mit Anhänger reserviert ist." - }, - "13": { - "then": "Dies ist ein Stellplatz, der für Carsharing reserviert ist." - }, "2": { "then": "Dies ist ein Behindertenstellplatz." }, @@ -7043,6 +7031,18 @@ }, "9": { "then": "Dies ist ein Stellplatz, der für Eltern mit Kindern reserviert ist." + }, + "10": { + "then": "Dies ist ein Stellplatz, der für das Personal reserviert ist." + }, + "11": { + "then": "Dies ist ein Stellplatz, der für Taxis reserviert ist." + }, + "12": { + "then": "Dies ist ein Stellplatz, der für Fahrzeuge mit Anhänger reserviert ist." + }, + "13": { + "then": "Dies ist ein Stellplatz, der für Carsharing reserviert ist." } }, "question": "Welche Art von Stellplatz ist dies?" @@ -7296,9 +7296,6 @@ "1": { "then": "Der Bodenbelag ist aus Sand" }, - "10": { - "then": "Die Oberfläche ist feiner Kies (weniger als 2 cm pro Stein)" - }, "2": { "then": "Der Bodenbelag ist aus Holzschnitzeln" }, @@ -7362,6 +7359,30 @@ "1": { "then": "Dies ist eine Struktur aus mehreren angeschlossenen Spielgeräten" }, + "2": { + "then": "Das ist eine Rutsche" + }, + "3": { + "then": "Dies ist ein Sandkasten" + }, + "4": { + "then": "Dies ist ein Springreiter" + }, + "5": { + "then": "Dies ist ein Kletterrahmen" + }, + "6": { + "then": "Dies ist eine Wippe" + }, + "7": { + "then": "Das ist ein Spielhaus" + }, + "8": { + "then": "Dies ist ein Karussell" + }, + "9": { + "then": "Dies ist eine Korbschaukel" + }, "10": { "then": "Dies ist ein Seilzug" }, @@ -7392,35 +7413,11 @@ "19": { "then": "Dies ist eine Jugendherberge" }, - "2": { - "then": "Das ist eine Rutsche" - }, "20": { "then": "Dies ist ein Trichter, mit dem man Trichterball spielen kann" }, "21": { "then": "Dies ist ein sich drehender Kreis" - }, - "3": { - "then": "Dies ist ein Sandkasten" - }, - "4": { - "then": "Dies ist ein Springreiter" - }, - "5": { - "then": "Dies ist ein Kletterrahmen" - }, - "6": { - "then": "Dies ist eine Wippe" - }, - "7": { - "then": "Das ist ein Spielhaus" - }, - "8": { - "then": "Dies ist ein Karussell" - }, - "9": { - "then": "Dies ist eine Korbschaukel" } }, "question": "Was ist das für ein Gerät?", @@ -7797,21 +7794,6 @@ "1": { "then": "2-Cent-Münzen werden akzeptiert" }, - "10": { - "then": "20-Centime-Münzen werden akzeptiert" - }, - "11": { - "then": "½-Schweizer Franken-Münzen werden akzeptiert" - }, - "12": { - "then": "1-Schweizer Franken-Münzen werden akzeptiert" - }, - "13": { - "then": "2-Schweizer Franken-Münzen werden akzeptiert" - }, - "14": { - "then": "5-Schweizer Franken-Münzen werden akzeptiert" - }, "2": { "then": "5-Cent-Münzen werden akzeptiert" }, @@ -7835,6 +7817,21 @@ }, "9": { "then": "10-Centime-Münzen werden akzeptiert" + }, + "10": { + "then": "20-Centime-Münzen werden akzeptiert" + }, + "11": { + "then": "½-Schweizer Franken-Münzen werden akzeptiert" + }, + "12": { + "then": "1-Schweizer Franken-Münzen werden akzeptiert" + }, + "13": { + "then": "2-Schweizer Franken-Münzen werden akzeptiert" + }, + "14": { + "then": "5-Schweizer Franken-Münzen werden akzeptiert" } }, "question": "Mit welchen Münzen kann man hier bezahlen?" @@ -7847,15 +7844,6 @@ "1": { "then": "10-Euro-Scheine werden angenommen" }, - "10": { - "then": "100-Schweizer Franken-Scheine werden akzeptiert" - }, - "11": { - "then": "200-Schweizer Franken-Scheine werden akzeptiert" - }, - "12": { - "then": "1000-Schweizer Franken-Scheine werden akzeptiert" - }, "2": { "then": "20-Euro-Scheine werden angenommen" }, @@ -7879,6 +7867,15 @@ }, "9": { "then": "50-Schweizer Franken-Scheine werden akzeptiert" + }, + "10": { + "then": "100-Schweizer Franken-Scheine werden akzeptiert" + }, + "11": { + "then": "200-Schweizer Franken-Scheine werden akzeptiert" + }, + "12": { + "then": "1000-Schweizer Franken-Scheine werden akzeptiert" } }, "question": "Mit welchen Banknoten kann man hier bezahlen?" @@ -8387,6 +8384,30 @@ "1": { "question": "Recycling von Batterien" }, + "2": { + "question": "Recycling von Getränkekartons" + }, + "3": { + "question": "Recycling von Dosen" + }, + "4": { + "question": "Recycling von Kleidung" + }, + "5": { + "question": "Recycling von Speiseöl" + }, + "6": { + "question": "Recycling von Motoröl" + }, + "7": { + "question": "Recycling von Leuchtstoffröhren" + }, + "8": { + "question": "Recycling von Grünabfällen" + }, + "9": { + "question": "Recycling von Glasflaschen" + }, "10": { "question": "Recycling von Glas" }, @@ -8417,9 +8438,6 @@ "19": { "question": "Recycling von Restabfällen" }, - "2": { - "question": "Recycling von Getränkekartons" - }, "20": { "question": "Recycling von Druckerpatronen" }, @@ -8428,27 +8446,6 @@ }, "22": { "question": "Recycling von Kunststoffverpackungen, Metallverpackungen und Getränkekartons (Tetrapak)" - }, - "3": { - "question": "Recycling von Dosen" - }, - "4": { - "question": "Recycling von Kleidung" - }, - "5": { - "question": "Recycling von Speiseöl" - }, - "6": { - "question": "Recycling von Motoröl" - }, - "7": { - "question": "Recycling von Leuchtstoffröhren" - }, - "8": { - "question": "Recycling von Grünabfällen" - }, - "9": { - "question": "Recycling von Glasflaschen" } } }, @@ -8516,6 +8513,30 @@ "1": { "then": "Getränkekartons können hier recycelt werden" }, + "2": { + "then": "Dosen können hier recycelt werden" + }, + "3": { + "then": "Kleidung kann hier recycelt werden" + }, + "4": { + "then": "Speiseöl kann hier recycelt werden" + }, + "5": { + "then": "Motoröl kann hier recycelt werden" + }, + "6": { + "then": "Hier können Leuchtstoffröhren recycelt werden" + }, + "7": { + "then": "Grünabfälle können hier recycelt werden" + }, + "8": { + "then": "Bio-Abfall kann hier recycelt werden" + }, + "9": { + "then": "Glasflaschen können hier recycelt werden" + }, "10": { "then": "Glas kann hier recycelt werden" }, @@ -8546,9 +8567,6 @@ "19": { "then": "Metallschrott kann hier recycelt werden" }, - "2": { - "then": "Dosen können hier recycelt werden" - }, "20": { "then": "Schuhe können hier recycelt werden" }, @@ -8566,27 +8584,6 @@ }, "25": { "then": "Fahrräder können hier recycelt werden" - }, - "3": { - "then": "Kleidung kann hier recycelt werden" - }, - "4": { - "then": "Speiseöl kann hier recycelt werden" - }, - "5": { - "then": "Motoröl kann hier recycelt werden" - }, - "6": { - "then": "Hier können Leuchtstoffröhren recycelt werden" - }, - "7": { - "then": "Grünabfälle können hier recycelt werden" - }, - "8": { - "then": "Bio-Abfall kann hier recycelt werden" - }, - "9": { - "then": "Glasflaschen können hier recycelt werden" } }, "question": "Was kann hier recycelt werden?" @@ -9807,12 +9804,6 @@ "1": { "then": "Diese Straßenlaterne verwendet LEDs" }, - "10": { - "then": "Diese Straßenlaterne verwendet Hochdruck-Natriumdampflampen (orange mit weiß)" - }, - "11": { - "then": "Diese Straßenlaterne wird mit Gas beleuchtet" - }, "2": { "then": "Diese Straßenlaterne verwendet Glühlampenlicht" }, @@ -9836,6 +9827,12 @@ }, "9": { "then": "Diese Straßenlaterne verwendet Niederdruck-Natriumdampflampen (einfarbig orange)" + }, + "10": { + "then": "Diese Straßenlaterne verwendet Hochdruck-Natriumdampflampen (orange mit weiß)" + }, + "11": { + "then": "Diese Straßenlaterne wird mit Gas beleuchtet" } }, "question": "Mit welcher Art von Beleuchtung arbeitet diese Straßenlaterne?" @@ -11403,6 +11400,30 @@ "1": { "question": "Verkauf von Getränken" }, + "2": { + "question": "Verkauf von Süßigkeiten" + }, + "3": { + "question": "Verkauf von Lebensmitteln" + }, + "4": { + "question": "Verkauf von Zigaretten" + }, + "5": { + "question": "Verkauf von Kondomen" + }, + "6": { + "question": "Verkauf von Kaffee" + }, + "7": { + "question": "Verkauf von Trinkwasser" + }, + "8": { + "question": "Verkauf von Zeitungen" + }, + "9": { + "question": "Verkauf von Fahrradschläuchen" + }, "10": { "question": "Verkauf von Milch" }, @@ -11433,9 +11454,6 @@ "19": { "question": "Verkauf von Blumen" }, - "2": { - "question": "Verkauf von Süßigkeiten" - }, "20": { "question": "Verkauf von Parkscheinen" }, @@ -11459,27 +11477,6 @@ }, "27": { "question": "Verkauf von Fahrradschlössern" - }, - "3": { - "question": "Verkauf von Lebensmitteln" - }, - "4": { - "question": "Verkauf von Zigaretten" - }, - "5": { - "question": "Verkauf von Kondomen" - }, - "6": { - "question": "Verkauf von Kaffee" - }, - "7": { - "question": "Verkauf von Trinkwasser" - }, - "8": { - "question": "Verkauf von Zeitungen" - }, - "9": { - "question": "Verkauf von Fahrradschläuchen" } } } @@ -11576,6 +11573,30 @@ "1": { "then": "Süßigkeiten werden verkauft" }, + "2": { + "then": "Lebensmittel werden verkauft" + }, + "3": { + "then": "Zigaretten werden verkauft" + }, + "4": { + "then": "Kondome werden verkauft" + }, + "5": { + "then": "Kaffee wird verkauft" + }, + "6": { + "then": "Trinkwasser wird verkauft" + }, + "7": { + "then": "Zeitungen werden verkauft" + }, + "8": { + "then": "Fahrradschläuche werden verkauft" + }, + "9": { + "then": "Milch wird verkauft" + }, "10": { "then": "Brot wird verkauft" }, @@ -11606,9 +11627,6 @@ "19": { "then": "Parkscheine werden verkauft" }, - "2": { - "then": "Lebensmittel werden verkauft" - }, "20": { "then": "Souvenirmünzen werden verkauft" }, @@ -11629,27 +11647,6 @@ }, "26": { "then": "Fahrradschlösser werden verkauft" - }, - "3": { - "then": "Zigaretten werden verkauft" - }, - "4": { - "then": "Kondome werden verkauft" - }, - "5": { - "then": "Kaffee wird verkauft" - }, - "6": { - "then": "Trinkwasser wird verkauft" - }, - "7": { - "then": "Zeitungen werden verkauft" - }, - "8": { - "then": "Fahrradschläuche werden verkauft" - }, - "9": { - "then": "Milch wird verkauft" } }, "question": "Was wird in diesem Automaten verkauft?", @@ -11950,4 +11947,4 @@ "render": "Windrad" } } -} +} \ No newline at end of file diff --git a/langs/layers/es.json b/langs/layers/es.json index 5007534d0..01e2be9ce 100644 --- a/langs/layers/es.json +++ b/langs/layers/es.json @@ -35,23 +35,6 @@ "1": { "title": "un mupi" }, - "10": { - "description": "Una pieza de tela impermeable con un mensaje impreso, anclada permanentemente en una pared", - "title": "una lona" - }, - "11": { - "title": "un tótem" - }, - "12": { - "description": "Se utiliza para carteles publicitarios, letreros de neón, logotipos y carteles en entradas institucionales", - "title": "un señal" - }, - "13": { - "title": "una escultura" - }, - "14": { - "title": "una pared pintada" - }, "2": { "title": "un mupi sobre la pared" }, @@ -77,6 +60,23 @@ }, "9": { "title": "una pantalla montada en una marquesina de tránsito" + }, + "10": { + "description": "Una pieza de tela impermeable con un mensaje impreso, anclada permanentemente en una pared", + "title": "una lona" + }, + "11": { + "title": "un tótem" + }, + "12": { + "description": "Se utiliza para carteles publicitarios, letreros de neón, logotipos y carteles en entradas institucionales", + "title": "un señal" + }, + "13": { + "title": "una escultura" + }, + "14": { + "title": "una pared pintada" } }, "tagRenderings": { @@ -171,9 +171,6 @@ "1": { "then": "Esto es un tablón de anuncios" }, - "10": { - "then": "Esto es una pared pintada" - }, "2": { "then": "Esto es una columna" }, @@ -197,6 +194,9 @@ }, "9": { "then": "Esto es un tótem" + }, + "10": { + "then": "Esto es una pared pintada" } }, "question": "¿Qué tipo de elemento publicitario es?", @@ -211,9 +211,6 @@ "1": { "then": "Tablon de anuncios" }, - "10": { - "then": "Pared Pintada" - }, "2": { "then": "Mupi" }, @@ -237,6 +234,9 @@ }, "9": { "then": "Tótem" + }, + "10": { + "then": "Pared Pintada" } } } @@ -375,15 +375,6 @@ "1": { "then": "Mural" }, - "10": { - "then": "Azulejo (Baldosas decorativas Españolas y Portuguesas)" - }, - "11": { - "then": "Cerámica" - }, - "12": { - "then": "Tallado en madera" - }, "2": { "then": "Pintura" }, @@ -407,6 +398,15 @@ }, "9": { "then": "Relieve" + }, + "10": { + "then": "Azulejo (Baldosas decorativas Españolas y Portuguesas)" + }, + "11": { + "then": "Cerámica" + }, + "12": { + "then": "Tallado en madera" } }, "question": "¿Qué tipo de obra es esta pieza?", @@ -1788,12 +1788,6 @@ "1": { "then": "Este carril bici está pavimentado" }, - "10": { - "then": "Este carril bici está hecho de gravilla" - }, - "12": { - "then": "Este carril bici está hecho de tierra natural" - }, "2": { "then": "Este carril bici está hecho de asfalto" }, @@ -1808,6 +1802,12 @@ }, "9": { "then": "Este carril bici está hecho de grava" + }, + "10": { + "then": "Este carril bici está hecho de gravilla" + }, + "12": { + "then": "Este carril bici está hecho de tierra natural" } }, "question": "¿De qué superficie está hecho este carril bici?", @@ -1853,9 +1853,6 @@ "1": { "then": "Este carril bici está pavimentado" }, - "10": { - "then": "Este carril bici está hecho de gravilla" - }, "2": { "then": "Este carril bici está hecho de asfalto" }, @@ -1867,6 +1864,9 @@ }, "9": { "then": "Este carril bici está hecho de grava" + }, + "10": { + "then": "Este carril bici está hecho de gravilla" } }, "question": "¿De qué esta hecha la superficie de esta calle?", @@ -2508,18 +2508,6 @@ "0": { "then": "Esto es una pizzería" }, - "10": { - "then": "Aquí se sirven platos Chinos" - }, - "11": { - "then": "Aquí se sirven platos Griegos" - }, - "12": { - "then": "Aquí se sirven platos Indios" - }, - "13": { - "then": "Aquí se sirven platos Turcos" - }, "2": { "then": "Principalmente sirve pasta" }, @@ -2540,6 +2528,18 @@ }, "9": { "then": "Aquí se sirven platos Franceses" + }, + "10": { + "then": "Aquí se sirven platos Chinos" + }, + "11": { + "then": "Aquí se sirven platos Griegos" + }, + "12": { + "then": "Aquí se sirven platos Indios" + }, + "13": { + "then": "Aquí se sirven platos Turcos" } }, "question": "¿Qué tipo de comida sirven aquí?", @@ -2850,11 +2850,11 @@ "tagRenderings": { "memorial-type": { "mappings": { - "10": { - "then": "Es una cruz" - }, "9": { "then": "Es un obelisco" + }, + "10": { + "then": "Es una cruz" } } } @@ -2945,19 +2945,6 @@ } } }, - "10": { - "options": { - "0": { - "question": "Todas las notas" - }, - "1": { - "question": "Ocultar las notas de importación" - }, - "2": { - "question": "Solo mostrar las notas de importación" - } - } - }, "2": { "options": { "0": { @@ -3013,6 +3000,19 @@ "question": "Solo mostrar las notas abiertas" } } + }, + "10": { + "options": { + "0": { + "question": "Todas las notas" + }, + "1": { + "question": "Ocultar las notas de importación" + }, + "2": { + "question": "Solo mostrar las notas de importación" + } + } } }, "name": "Notas de OpenStreetMap", @@ -3615,6 +3615,24 @@ "1": { "question": "Reciclaje de baterías" }, + "3": { + "question": "Reciclaje de latas" + }, + "4": { + "question": "Reciclaje de ropa" + }, + "5": { + "question": "Reciclaje de aceite de cocina" + }, + "6": { + "question": "Reciclaje de aceite de motor" + }, + "8": { + "question": "Reciclaje de residuos orgánicos" + }, + "9": { + "question": "Reciclaje de botellas de cristal" + }, "10": { "question": "Reciclaje de cristal" }, @@ -3641,24 +3659,6 @@ }, "18": { "question": "Reciclaje de pequeños electrodomésticos" - }, - "3": { - "question": "Reciclaje de latas" - }, - "4": { - "question": "Reciclaje de ropa" - }, - "5": { - "question": "Reciclaje de aceite de cocina" - }, - "6": { - "question": "Reciclaje de aceite de motor" - }, - "8": { - "question": "Reciclaje de residuos orgánicos" - }, - "9": { - "question": "Reciclaje de botellas de cristal" } } } @@ -3701,6 +3701,27 @@ "0": { "then": "Aquí se pueden reciclar baterías" }, + "2": { + "then": "Aquí se pueden reciclar latas" + }, + "3": { + "then": "Aquí se puede reciclar ropa" + }, + "4": { + "then": "Aquí se puede reciclar aceite de cocina" + }, + "5": { + "then": "Aquí se puede reciclar aceite de motor" + }, + "7": { + "then": "Los residuos orgánicos pueden reciclarse aquí" + }, + "8": { + "then": "Aquí se pueden reciclar residuos orgánicos" + }, + "9": { + "then": "Aquí se pueden reciclar botellas de cristal" + }, "10": { "then": "Aquí se puede reciclar cristal" }, @@ -3725,29 +3746,8 @@ "19": { "then": "Aquí se puede reciclar chatarra" }, - "2": { - "then": "Aquí se pueden reciclar latas" - }, "20": { "then": "El calzado se puede reciclar aquí" - }, - "3": { - "then": "Aquí se puede reciclar ropa" - }, - "4": { - "then": "Aquí se puede reciclar aceite de cocina" - }, - "5": { - "then": "Aquí se puede reciclar aceite de motor" - }, - "7": { - "then": "Los residuos orgánicos pueden reciclarse aquí" - }, - "8": { - "then": "Aquí se pueden reciclar residuos orgánicos" - }, - "9": { - "then": "Aquí se pueden reciclar botellas de cristal" } }, "question": "¿Qué se puede reciclar aquí?" @@ -4168,12 +4168,6 @@ "1": { "then": "Esta lámpara utiliza LEDs" }, - "10": { - "then": "Esta lámpara utiliza lámparas de sodio de alta presión (naranja con blanco)" - }, - "11": { - "then": "Esta lampara se ilumina con gas" - }, "2": { "then": "Esta lámpara utiliza iluminación incandescente" }, @@ -4194,6 +4188,12 @@ }, "9": { "then": "Esta lámpara utiliza lámparas de sodio de baja presión (naranja monocromo)" + }, + "10": { + "then": "Esta lámpara utiliza lámparas de sodio de alta presión (naranja con blanco)" + }, + "11": { + "then": "Esta lampara se ilumina con gas" } }, "question": "¿Qué tipo de iluminación utiliza esta lámpara?" @@ -4774,6 +4774,9 @@ }, "vending": { "mappings": { + "8": { + "then": "Aquí se venden cámaras de aire para bicicletas" + }, "22": { "then": "Las luces para bicicletas se venden aquí" }, @@ -4788,9 +4791,6 @@ }, "26": { "then": "Aquí se venden candados para bicicletas" - }, - "8": { - "then": "Aquí se venden cámaras de aire para bicicletas" } } } @@ -4878,4 +4878,4 @@ } } } -} +} \ No newline at end of file diff --git a/public/css/index-tailwind-output.css b/public/css/index-tailwind-output.css index 506dc50b3..f84b175b8 100644 --- a/public/css/index-tailwind-output.css +++ b/public/css/index-tailwind-output.css @@ -711,14 +711,6 @@ video { top: 2.5rem; } -.top-2 { - top: 0.5rem; -} - -.right-2 { - right: 0.5rem; -} - .left-1\/4 { left: 25%; } @@ -787,6 +779,10 @@ video { top: 0.25rem; } +.top-2 { + top: 0.5rem; +} + .top-\[calc\(100\%\+1rem\)\] { top: calc(100% + 1rem); } @@ -1077,10 +1073,6 @@ video { margin-left: -1.5rem; } -.mt-12 { - margin-top: 3rem; -} - .mb-3 { margin-bottom: 0.75rem; } @@ -1448,6 +1440,10 @@ video { max-height: 3rem; } +.max-h-96 { + max-height: 24rem; +} + .max-h-24 { max-height: 6rem; } @@ -5298,11 +5294,6 @@ svg.apply-fill path { border-color: rgb(209 213 219 / var(--tw-border-opacity)); } -.hover\:bg-stone-200:hover { - --tw-bg-opacity: 1; - background-color: rgb(231 229 228 / var(--tw-bg-opacity)); -} - .hover\:bg-indigo-200:hover { --tw-bg-opacity: 1; background-color: rgb(199 210 254 / var(--tw-bg-opacity)); @@ -7243,6 +7234,10 @@ svg.apply-fill path { width: 50%; } + .sm\:w-96 { + width: 24rem; + } + .sm\:w-11 { width: 2.75rem; } @@ -7259,10 +7254,6 @@ svg.apply-fill path { width: 1.5rem; } - .sm\:w-96 { - width: 24rem; - } - .sm\:grid-cols-2 { grid-template-columns: repeat(2, minmax(0, 1fr)); } diff --git a/src/Logic/GeoOperations.ts b/src/Logic/GeoOperations.ts index 0cd7dd658..51423fc29 100644 --- a/src/Logic/GeoOperations.ts +++ b/src/Logic/GeoOperations.ts @@ -909,6 +909,7 @@ export class GeoOperations { /** * GeoOperations.distanceToHuman(52.3) // => "50m" + * GeoOperations.distanceToHuman(999) // => "1.0km" * GeoOperations.distanceToHuman(2800) // => "2.8km" * GeoOperations.distanceToHuman(12800) // => "13km" * GeoOperations.distanceToHuman(128000) // => "130km" @@ -920,7 +921,7 @@ export class GeoOperations { if (meters === undefined) { return "" } - meters = Math.round(meters) + meters = Utils.roundHuman( Math.round(meters)) if (meters < 1000) { return Utils.roundHuman(meters) + "m" } diff --git a/src/Logic/Geocoding/CombinedSearcher.ts b/src/Logic/Geocoding/CombinedSearcher.ts index e2136e7fb..bd2539f14 100644 --- a/src/Logic/Geocoding/CombinedSearcher.ts +++ b/src/Logic/Geocoding/CombinedSearcher.ts @@ -1,5 +1,6 @@ import GeocodingProvider, { GeoCodeResult, GeocodingOptions } from "./GeocodingProvider" import { Utils } from "../../Utils" +import { Store, Stores } from "../UIEventSource" export default class CombinedSearcher implements GeocodingProvider { private _providers: ReadonlyArray @@ -16,13 +17,13 @@ export default class CombinedSearcher implements GeocodingProvider { * @param geocoded * @private */ - private merge(geocoded: GeoCodeResult[][]): GeoCodeResult[]{ - const results : GeoCodeResult[] = [] + private merge(geocoded: GeoCodeResult[][]): GeoCodeResult[] { + const results: GeoCodeResult[] = [] const seenIds = new Set() for (const geocodedElement of geocoded) { for (const entry of geocodedElement) { - const id = entry.osm_type+ entry.osm_id - if(seenIds.has(id)){ + const id = entry.osm_type + entry.osm_id + if (seenIds.has(id)) { continue } seenIds.add(id) @@ -33,12 +34,12 @@ export default class CombinedSearcher implements GeocodingProvider { } async search(query: string, options?: GeocodingOptions): Promise { - const results = await Promise.all(this._providers.map(pr => pr.search(query, options))) - return this.merge(results) + const results = (await Promise.all(this._providers.map(pr => pr.search(query, options)))) + return results.flatMap(x => x) } - async suggest(query: string, options?: GeocodingOptions): Promise { - const results = await Promise.all(this._providersWithSuggest.map(pr => pr.suggest(query, options))) - return this.merge(results) + suggest(query: string, options?: GeocodingOptions): Store { + return Stores.concat(this._providersWithSuggest.map(pr => pr.suggest(query, options))) + } } diff --git a/src/Logic/Geocoding/CoordinateSearch.ts b/src/Logic/Geocoding/CoordinateSearch.ts index 2d972604a..7a28fae74 100644 --- a/src/Logic/Geocoding/CoordinateSearch.ts +++ b/src/Logic/Geocoding/CoordinateSearch.ts @@ -1,5 +1,6 @@ import GeocodingProvider, { GeoCodeResult, GeocodingOptions } from "./GeocodingProvider" import { Utils } from "../../Utils" +import { ImmutableStore, Store } from "../UIEventSource" /** * A simple search-class which interprets possible locations @@ -17,28 +18,25 @@ export default class CoordinateSearch implements GeocodingProvider { ] /** - * - * @param query - * @param options * * const ls = new CoordinateSearch() - * const results = await ls.search("https://www.openstreetmap.org/search?query=Brugge#map=11/51.2611/3.2217") + * const results = ls.directSearch("https://www.openstreetmap.org/search?query=Brugge#map=11/51.2611/3.2217") * results.length // => 1 * results[0] // => {lat: 51.2611, lon: 3.2217, display_name: "lon: 3.2217, lat: 51.2611", "category": "coordinate","source": "coordinateSearch"} * * const ls = new CoordinateSearch() - * const results = await ls.search("https://www.openstreetmap.org/#map=11/51.2611/3.2217") + * const results = ls.directSearch("https://www.openstreetmap.org/#map=11/51.2611/3.2217") * results.length // => 1 * results[0] // => {lat: 51.2611, lon: 3.2217, display_name: "lon: 3.2217, lat: 51.2611", "category": "coordinate","source": "coordinateSearch"} * * const ls = new CoordinateSearch() - * const results = await ls.search("51.2611 3.2217") + * const results = ls.directSearch("51.2611 3.2217") * results.length // => 2 * results[0] // => {lat: 51.2611, lon: 3.2217, display_name: "lon: 3.2217, lat: 51.2611", "category": "coordinate", "source": "coordinateSearch"} * results[1] // => {lon: 51.2611, lat: 3.2217, display_name: "lon: 51.2611, lat: 3.2217", "category": "coordinate", "source": "coordinateSearch"} * */ - async search(query: string, options?: GeocodingOptions): Promise { + private directSearch(query: string): GeoCodeResult[] { const matches = Utils.NoNull(CoordinateSearch.latLonRegexes.map(r => query.match(r))).map(m => { lat: Number(m[1]), @@ -49,8 +47,7 @@ export default class CoordinateSearch implements GeocodingProvider { }) - - const matchesLonLat = Utils.NoNull(CoordinateSearch.lonLatRegexes.map(r => query.match(r))) + const matchesLonLat = Utils.NoNull(CoordinateSearch.lonLatRegexes.map(r => query.match(r))) .map(m => { lat: Number(m[2]), lon: Number(m[1]), @@ -58,12 +55,15 @@ export default class CoordinateSearch implements GeocodingProvider { source: "coordinateSearch", category: "coordinate" }) - return matches.concat(matchesLonLat) } - suggest(query: string, options?: GeocodingOptions): Promise { - return this.search(query, options) + suggest(query: string): Store { + return new ImmutableStore(this.directSearch(query)) + } + + async search (query: string): Promise { + return this.directSearch(query) } } diff --git a/src/Logic/Geocoding/GeocodingProvider.ts b/src/Logic/Geocoding/GeocodingProvider.ts index 16ef72648..56f9c9b60 100644 --- a/src/Logic/Geocoding/GeocodingProvider.ts +++ b/src/Logic/Geocoding/GeocodingProvider.ts @@ -1,6 +1,7 @@ import { BBox } from "../BBox" import { Feature, Geometry } from "geojson" import { DefaultPinIcon } from "../../Models/Constants" +import { Store } from "../UIEventSource" export type GeocodingCategory = "coordinate" | "city" | "house" | "street" | "locality" | "country" | "train_station" | "county" | "airport" @@ -42,7 +43,7 @@ export default interface GeocodingProvider { * @param query * @param options */ - suggest?(query: string, options?: GeocodingOptions): Promise + suggest?(query: string, options?: GeocodingOptions): Store } export type ReverseGeocodingResult = Feature { - return this.searchEntries(query, options, false) + return this.searchEntries(query, options, false).data } - searchEntries(query: string, options?: GeocodingOptions, matchStart?: boolean): GeoCodeResult[] { + private getPartialResult(query: string, matchStart: boolean, centerpoint: [number, number], features: Feature[]): IntermediateResult[] { + const results: IntermediateResult [] = [] + + for (const feature of features) { + const props = feature.properties + const searchTerms: string[] = Utils.NoNull([props.name, props.alt_name, props.local_name, + (props["addr:street"] && props["addr:number"]) ? + props["addr:street"] + props["addr:number"] : undefined]) + + + const levehnsteinD = Math.min(...searchTerms.flatMap(entry => entry.split(/ /)).map(entry => { + let simplified = Utils.simplifyStringForSearch(entry) + if (matchStart) { + simplified = simplified.slice(0, query.length) + } + return Utils.levenshteinDistance(query, simplified) + })) + const center = GeoOperations.centerpointCoordinates(feature) + if (levehnsteinD <= 2) { + + let description = "" + if (feature.properties["addr:street"]) { + description += "" + feature.properties["addr:street"] + } + if (feature.properties["addr:housenumber"]) { + description += " " + feature.properties["addr:housenumber"] + } + results.push({ + feature, + center, + physicalDistance: GeoOperations.distanceBetween(centerpoint, center), + levehnsteinD, + searchTerms, + description: description !== "" ? description : undefined + }) + } + } + return results + } + + searchEntries(query: string, options?: GeocodingOptions, matchStart?: boolean): Store { if (query.length < 3) { - return [] + return new ImmutableStore([]) } const center: { lon: number; lat: number } = this._state.mapProperties.location.data const centerPoint: [number, number] = [center.lon, center.lat] - let results: { - feature: Feature, - /** - * Lon, lat - */ - center: [number, number], - levehnsteinD: number, - physicalDistance: number, - searchTerms: string[], - description: string - }[] = [] const properties = this._state.perLayer query = Utils.simplifyStringForSearch(query) + + const partials: Store[] = [] + for (const [_, geoIndexedStore] of properties) { - for (const feature of geoIndexedStore.features.data) { - const props = feature.properties - const searchTerms: string[] = Utils.NoNull([props.name, props.alt_name, props.local_name, - (props["addr:street"] && props["addr:number"]) ? - props["addr:street"] + props["addr:number"] : undefined]) + const partialResult = geoIndexedStore.features.map(features => this.getPartialResult(query, matchStart, centerPoint, features)) + partials.push(partialResult) + } - - const levehnsteinD = Math.min(...searchTerms.flatMap(entry => entry.split(/ /)).map(entry => { - let simplified = Utils.simplifyStringForSearch(entry) - if (matchStart) { - simplified = simplified.slice(0, query.length) - } - return Utils.levenshteinDistance(query, simplified) - })) - const center = GeoOperations.centerpointCoordinates(feature) - if (levehnsteinD <= 2) { - - let description = "" - function ifDef(prefix: string, key: string){ - if(feature.properties[key]){ - description += prefix+ feature.properties[key] - } - } - ifDef("", "addr:street") - ifDef(" ", "addr:housenumber") - results.push({ - feature, - center, - physicalDistance: GeoOperations.distanceBetween(centerPoint, center), - levehnsteinD, - searchTerms, - description: description !== "" ? description : undefined - }) + const listed: Store = Stores.concat(partials) + return listed.mapD(results => { + results.sort((a, b) => (a.physicalDistance + a.levehnsteinD * 25) - (b.physicalDistance + b.levehnsteinD * 25)) + if (this._limit || options?.limit) { + results = results.slice(0, Math.min(this._limit ?? options?.limit, options?.limit ?? this._limit)) + } + return results.map(entry => { + const id = entry.feature.properties.id.split("/") + return { + lon: entry.center[0], + lat: entry.center[1], + osm_type: id[0], + osm_id: id[1], + display_name: entry.searchTerms[0], + source: "localElementSearch", + feature: entry.feature, + importance: 1, + description: entry.description } - } - } - results.sort((a, b) => (a.physicalDistance + a.levehnsteinD * 25) - (b.physicalDistance + b.levehnsteinD * 25)) - if (this._limit || options?.limit) { - results = results.slice(0, Math.min(this._limit ?? options?.limit, options?.limit ?? this._limit)) - } - return results.map(entry => { - const id = entry.feature.properties.id.split("/") - return { - lon: entry.center[0], - lat: entry.center[1], - osm_type: id[0], - osm_id: id[1], - display_name: entry.searchTerms[0], - source: "localElementSearch", - feature: entry.feature, - importance: 1, - description: entry.description - } + }) }) + + } - async suggest(query: string, options?: GeocodingOptions): Promise { + suggest(query: string, options?: GeocodingOptions): Store { return this.searchEntries(query, options, true) } diff --git a/src/Logic/Geocoding/NominatimGeocoding.ts b/src/Logic/Geocoding/NominatimGeocoding.ts index f693a0f4e..691ba8b1d 100644 --- a/src/Logic/Geocoding/NominatimGeocoding.ts +++ b/src/Logic/Geocoding/NominatimGeocoding.ts @@ -3,9 +3,10 @@ import { BBox } from "../BBox" import Constants from "../../Models/Constants" import { FeatureCollection } from "geojson" import Locale from "../../UI/i18n/Locale" -import GeocodingProvider, { GeoCodeResult, ReverseGeocodingProvider } from "./GeocodingProvider" +import GeocodingProvider, { GeoCodeResult } from "./GeocodingProvider" +import { Store, UIEventSource } from "../UIEventSource" -export class NominatimGeocoding implements GeocodingProvider, ReverseGeocodingProvider { +export class NominatimGeocoding implements GeocodingProvider { private readonly _host ; @@ -13,14 +14,14 @@ export class NominatimGeocoding implements GeocodingProvider, ReverseGeocodingPr this._host = host } - public async search(query: string, options?: { bbox?: BBox; limit?: number }): Promise { + public search(query: string, options?: { bbox?: BBox; limit?: number }): Promise { const b = options?.bbox ?? BBox.global const url = `${ this._host }search?format=json&limit=${options?.limit ?? 1}&viewbox=${b.getEast()},${b.getNorth()},${b.getWest()},${b.getSouth()}&accept-language=${ Locale.language.data }&q=${query}` - return await Utils.downloadJson(url) + return Utils.downloadJson(url) } diff --git a/src/Logic/Geocoding/PhotonSearch.ts b/src/Logic/Geocoding/PhotonSearch.ts index f268a1fab..286fecb4e 100644 --- a/src/Logic/Geocoding/PhotonSearch.ts +++ b/src/Logic/Geocoding/PhotonSearch.ts @@ -1,6 +1,7 @@ import Constants from "../../Models/Constants" import GeocodingProvider, { - GeoCodeResult, GeocodingCategory, + GeoCodeResult, + GeocodingCategory, GeocodingOptions, ReverseGeocodingProvider, ReverseGeocodingResult @@ -9,6 +10,7 @@ import { Utils } from "../../Utils" import { Feature, FeatureCollection } from "geojson" import Locale from "../../UI/i18n/Locale" import { GeoOperations } from "../GeoOperations" +import { Store, Stores } from "../UIEventSource" export default class PhotonSearch implements GeocodingProvider, ReverseGeocodingProvider { private _endpoint: string @@ -52,8 +54,8 @@ export default class PhotonSearch implements GeocodingProvider, ReverseGeocoding } - search(query: string, options?: GeocodingOptions): Promise { - return this.suggest(query, options) + suggest(query: string, options?: GeocodingOptions): Store { + return Stores.FromPromise(this.search(query, options)) } private buildDescription(entry: Feature) { @@ -71,7 +73,7 @@ export default class PhotonSearch implements GeocodingProvider, ReverseGeocoding case "house": { const addr = ifdef("", p.street) + ifdef(" ", p.housenumber) - if(!addr){ + if (!addr) { return p.city } return addr + ifdef(", ", p.city) @@ -81,8 +83,8 @@ export default class PhotonSearch implements GeocodingProvider, ReverseGeocoding return p.city ?? p.country case "city": case "locality": - if(p.state){ - return p.state + ifdef(", ", p.country) + if (p.state) { + return p.state + ifdef(", ", p.country) } return p.country case "country": @@ -91,18 +93,18 @@ export default class PhotonSearch implements GeocodingProvider, ReverseGeocoding } - private getCategory(entry: Feature){ + private getCategory(entry: Feature) { const p = entry.properties - if(p.osm_value === "train_station" || p.osm_key === "railway"){ + if (p.osm_value === "train_station" || p.osm_key === "railway") { return "train_station" } - if(p.osm_value === "aerodrome" || p.osm_key === "aeroway"){ + if (p.osm_value === "aerodrome" || p.osm_key === "aeroway") { return "airport" } return p.type } - async suggest?(query: string, options?: GeocodingOptions): Promise { + async search(query: string, options?: GeocodingOptions): Promise { if (query.length < 3) { return [] } diff --git a/src/Logic/Geocoding/RecentSearch.ts b/src/Logic/Geocoding/RecentSearch.ts index 070ac7b34..ab738c9d2 100644 --- a/src/Logic/Geocoding/RecentSearch.ts +++ b/src/Logic/Geocoding/RecentSearch.ts @@ -18,12 +18,15 @@ export class RecentSearch { state.selectedElement.addCallbackAndRunD(selected => { + const [osm_type, osm_id] = selected.properties.id.split("/") + if(!osm_id){ + return + } const [lon, lat] = GeoOperations.centerpointCoordinates(selected) const entry = { feature: selected, osm_id, osm_type, - description: "Viewed recently", lon, lat } this.addSelected(entry) diff --git a/src/Logic/Geocoding/ThemeSearch.ts b/src/Logic/Geocoding/ThemeSearch.ts index 7806138bd..4b7331382 100644 --- a/src/Logic/Geocoding/ThemeSearch.ts +++ b/src/Logic/Geocoding/ThemeSearch.ts @@ -4,7 +4,7 @@ import { MinimalLayoutInformation } from "../../Models/ThemeConfig/LayoutConfig" import { SpecialVisualizationState } from "../../UI/SpecialVisualization" import { Utils } from "../../Utils" import MoreScreen from "../../UI/BigComponents/MoreScreen" -import { Store } from "../UIEventSource" +import { ImmutableStore, Store } from "../UIEventSource" export default class ThemeSearch implements GeocodingProvider { @@ -17,11 +17,15 @@ export default class ThemeSearch implements GeocodingProvider { this._knownHiddenThemes = MoreScreen.knownHiddenThemes(this._state.osmConnection) } - search(query: string, options?: GeocodingOptions): Promise { - return this.suggest(query, options) + async search(query: string, options?: GeocodingOptions): Promise { + return this.searchDirect(query, options) } - async suggest?(query: string, options?: GeocodingOptions): Promise { + suggest(query: string, options?: GeocodingOptions): Store { + return new ImmutableStore(this.searchDirect(query, options)) + } + + private searchDirect(query: string, options?: GeocodingOptions): GeoCodeResult[] { if(query.length < 1){ return [] } @@ -33,10 +37,10 @@ export default class ThemeSearch implements GeocodingProvider { .filter(th => MoreScreen.MatchesLayout(th, query)) .slice(0, limit + 1) - return withMatch.map(match => ( { + return withMatch.map(match => { payload: match, osm_id: match.id - })) + }) } diff --git a/src/Logic/State/UserSettingsMetaTagging.ts b/src/Logic/State/UserSettingsMetaTagging.ts index 6e568c5c3..33a5ae85b 100644 --- a/src/Logic/State/UserSettingsMetaTagging.ts +++ b/src/Logic/State/UserSettingsMetaTagging.ts @@ -1,42 +1,14 @@ import { Utils } from "../../Utils" /** This code is autogenerated - do not edit. Edit ./assets/layers/usersettings/usersettings.json instead */ export class ThemeMetaTagging { - public static readonly themeName = "usersettings" + public static readonly themeName = "usersettings" - public metaTaggging_for_usersettings(feat: { properties: Record }) { - Utils.AddLazyProperty(feat.properties, "_mastodon_candidate_md", () => - feat.properties._description - .match(/\[[^\]]*\]\((.*(mastodon|en.osm.town).*)\).*/) - ?.at(1) - ) - Utils.AddLazyProperty( - feat.properties, - "_d", - () => feat.properties._description?.replace(/</g, "<")?.replace(/>/g, ">") ?? "" - ) - Utils.AddLazyProperty(feat.properties, "_mastodon_candidate_a", () => - ((feat) => { - const e = document.createElement("div") - e.innerHTML = feat.properties._d - return Array.from(e.getElementsByTagName("a")).filter( - (a) => a.href.match(/mastodon|en.osm.town/) !== null - )[0]?.href - })(feat) - ) - Utils.AddLazyProperty(feat.properties, "_mastodon_link", () => - ((feat) => { - const e = document.createElement("div") - e.innerHTML = feat.properties._d - return Array.from(e.getElementsByTagName("a")).filter( - (a) => a.getAttribute("rel")?.indexOf("me") >= 0 - )[0]?.href - })(feat) - ) - Utils.AddLazyProperty( - feat.properties, - "_mastodon_candidate", - () => feat.properties._mastodon_candidate_md ?? feat.properties._mastodon_candidate_a - ) - feat.properties["__current_backgroun"] = "initial_value" - } -} + public metaTaggging_for_usersettings(feat: {properties: Record}) { + Utils.AddLazyProperty(feat.properties, '_mastodon_candidate_md', () => feat.properties._description.match(/\[[^\]]*\]\((.*(mastodon|en.osm.town).*)\).*/)?.at(1) ) + Utils.AddLazyProperty(feat.properties, '_d', () => feat.properties._description?.replace(/</g,'<')?.replace(/>/g,'>') ?? '' ) + Utils.AddLazyProperty(feat.properties, '_mastodon_candidate_a', () => (feat => {const e = document.createElement('div');e.innerHTML = feat.properties._d;return Array.from(e.getElementsByTagName("a")).filter(a => a.href.match(/mastodon|en.osm.town/) !== null)[0]?.href }) (feat) ) + Utils.AddLazyProperty(feat.properties, '_mastodon_link', () => (feat => {const e = document.createElement('div');e.innerHTML = feat.properties._d;return Array.from(e.getElementsByTagName("a")).filter(a => a.getAttribute("rel")?.indexOf('me') >= 0)[0]?.href})(feat) ) + Utils.AddLazyProperty(feat.properties, '_mastodon_candidate', () => feat.properties._mastodon_candidate_md ?? feat.properties._mastodon_candidate_a ) + feat.properties['__current_backgroun'] = 'initial_value' + } +} \ No newline at end of file diff --git a/src/Logic/UIEventSource.ts b/src/Logic/UIEventSource.ts index cb2cfc773..38f1a2f04 100644 --- a/src/Logic/UIEventSource.ts +++ b/src/Logic/UIEventSource.ts @@ -41,8 +41,26 @@ export class Stores { return src } - public static flatten(source: Store>, possibleSources?: Store[]): Store { - return UIEventSource.flatten(source, possibleSources) + public static concat(stores: Store[]): Store { + const newStore = new UIEventSource([]) + function update(){ + if(newStore._callbacks.isDestroyed){ + return true // unregister + } + const results: T[] = [] + for (const store of stores) { + if(store.data){ + results.push(...store.data) + } + } + newStore.setData(results) + } + + for (const store of stores) { + store.addCallback(() => update()) + } + update() + return newStore } /** @@ -105,8 +123,6 @@ export abstract class Store implements Readable { callbackDestroyFunction: (f: () => void) => void ): Store - M - public mapD( f: (t: Exclude) => J, extraStoresToWatch?: Store[], @@ -120,7 +136,7 @@ export abstract class Store implements Readable { return null } return f(>t) - }, extraStoresToWatch) + }, extraStoresToWatch, callbackDestroyFunction) } /** @@ -231,6 +247,9 @@ export abstract class Store implements Readable { if (mapped.data === newEventSource) { sink.setData(resultData) } + if(sink._callbacks.isDestroyed){ + return true // unregister + } }) }) @@ -308,6 +327,8 @@ export abstract class Store implements Readable { run(v) }) } + + public abstract destroy() } export class ImmutableStore extends Store { @@ -361,6 +382,10 @@ export class ImmutableStore extends Store { bind(f: (t: T) => Store): Store { return f(this.data) } + + destroy() { + // pass + } } /** @@ -369,7 +394,7 @@ export class ImmutableStore extends Store { class ListenerTracker { public pingCount = 0 private readonly _callbacks: ((t: T) => boolean | void | any)[] = [] - +public isDestroyed = false /** * Adds a callback which can be called; a function to unregister is returned */ @@ -429,6 +454,11 @@ class ListenerTracker { length() { return this._callbacks.length } + + public destroy(){ + this.isDestroyed= true + this._callbacks.splice(0, this._callbacks.length) + } } /** @@ -584,10 +614,14 @@ class MappedStore extends Store { this._data = newData this._callbacks.ping(this._data) } + + destroy() { + this.unregisterFromUpstream() + } } export class UIEventSource extends Store implements Writable { - private static readonly pass: () => {} + private static readonly pass: (() => void) = () => {}; public data: T _callbacks: ListenerTracker = new ListenerTracker() @@ -596,9 +630,13 @@ export class UIEventSource extends Store implements Writable { this.data = data } + public destroy(){ + this._callbacks.destroy() + } + public static flatten( source: Store>, - possibleSources?: Store[] + possibleSources?: Store[] ): UIEventSource { const sink = new UIEventSource(source.data?.data) @@ -627,7 +665,7 @@ export class UIEventSource extends Store implements Writable { */ public static FromPromise( promise: Promise, - onError: (e: any) => void = undefined + onError: (e) => void = undefined ): UIEventSource { const src = new UIEventSource(undefined) promise?.then((d) => src.setData(d)) @@ -671,7 +709,7 @@ export class UIEventSource extends Store implements Writable { public static asInt(source: UIEventSource): UIEventSource { return source.sync( (str) => { - let parsed = parseInt(str) + const parsed = parseInt(str) return isNaN(parsed) ? undefined : parsed }, [], @@ -702,7 +740,7 @@ export class UIEventSource extends Store implements Writable { public static asFloat(source: UIEventSource): UIEventSource { return source.sync( (str) => { - let parsed = parseFloat(str) + const parsed = parseFloat(str) return isNaN(parsed) ? undefined : parsed }, [], diff --git a/src/Models/ThemeViewState.ts b/src/Models/ThemeViewState.ts index 3c917d8ea..b94d0a312 100644 --- a/src/Models/ThemeViewState.ts +++ b/src/Models/ThemeViewState.ts @@ -601,6 +601,9 @@ export default class ThemeViewState implements SpecialVisualizationState { ) { return } + if(document.activeElement.tagName === "button" || document.activeElement.tagName === "input"){ + return + } this.selectClosestAtCenter(0) } ) diff --git a/src/UI/Base/ModalRight.svelte b/src/UI/Base/ModalRight.svelte index dea9fc306..3d8eceb41 100644 --- a/src/UI/Base/ModalRight.svelte +++ b/src/UI/Base/ModalRight.svelte @@ -13,7 +13,7 @@ autofocus class="normal-background absolute top-0 right-0 flex h-screen w-full flex-col overflow-y-auto drop-shadow-2xl md:w-6/12 lg:w-5/12 xl:w-4/12" role="dialog" - style="max-width: 100vw; max-height: 100vh" + style="max-width: 100vw; max-height: 100vh; z-index: 11" tabindex="-1" id="modal-right" > diff --git a/src/UI/BigComponents/Geosearch.svelte b/src/UI/BigComponents/Geosearch.svelte index e36054d06..89c588657 100644 --- a/src/UI/BigComponents/Geosearch.svelte +++ b/src/UI/BigComponents/Geosearch.svelte @@ -12,11 +12,15 @@ import { ariaLabel } from "../../Utils/ariaLabel" import { GeoLocationState } from "../../Logic/State/GeoLocationState" import { NominatimGeocoding } from "../../Logic/Geocoding/NominatimGeocoding" - import type GeocodingProvider from "../../Logic/Geocoding/GeocodingProvider" + import { GeocodingUtils } from "../../Logic/Geocoding/GeocodingProvider" import type { GeoCodeResult } from "../../Logic/Geocoding/GeocodingProvider" + import type GeocodingProvider from "../../Logic/Geocoding/GeocodingProvider" import SearchResults from "./SearchResults.svelte" import type { SpecialVisualizationState } from "../SpecialVisualization" + import MoreScreen from "./MoreScreen" + import type { MinimalLayoutInformation } from "../../Models/ThemeConfig/LayoutConfig" + import { focusWithArrows } from "../../Utils/focusWithArrows" export let perLayer: ReadonlyMap | undefined = undefined export let bounds: UIEventSource @@ -82,13 +86,27 @@ return } const poi = result[0] - const [lat0, lat1, lon0, lon1] = poi.boundingbox - bounds.set( - new BBox([ - [lon0, lat0], - [lon1, lat1] - ]).pad(0.01) - ) + if (poi.payload !== undefined) { + // This is a theme + const theme = poi.payload + const url = MoreScreen.createUrlFor(theme, false) + console.log("Found a theme, going to", url) + // @ts-ignore + window.location = url + return + } + if (poi.boundingbox) { + + const [lat0, lat1, lon0, lon1] = poi.boundingbox + bounds.set( + new BBox([ + [lon0, lat0], + [lon1, lat1] + ]).pad(0.01) + ) + } else if (poi.lon && poi.lat) { + state.mapProperties.flyTo(poi.lon, poi.lat, GeocodingUtils.categoryToZoomLevel[poi.category] ?? 16) + } if (perLayer !== undefined) { const id = poi.osm_type + "/" + poi.osm_id const layers = Array.from(perLayer?.values() ?? []) @@ -107,6 +125,7 @@ } dispatch("searchIsValid", false) dispatch("searchCompleted") + isFocused.setData(false) } catch (e) { console.error(e) feedback = Translations.t.general.search.error.txt @@ -116,45 +135,73 @@ } } - let suggestions: Store<{success: GeoCodeResult[]} | {error}> = searchContents.stabilized(250).bindD(search => - UIEventSource.FromPromiseWithErr(searcher.suggest(search)) + let suggestions: Store = searchContents.stabilized(250).bindD(search => { + if (search.length === 0) { + return undefined + } + return searcher.suggest(search, { bbox: bounds.data }) + } ) + let geosearch: HTMLDivElement + + function checkFocus() { + window.requestAnimationFrame(() => { + if (geosearch.contains(document.activeElement)) { + return + } + isFocused.setData(false) + }) + } + + document.addEventListener("focus",() => { + checkFocus() + }, true /* use 'capturing' instead of bubbling, needed for focus-events*/) + + + -
-
- {#if isRunning} - {Translations.t.general.search.searching} - {:else} - { +
+ +
+ + {#if isRunning} + {Translations.t.general.search.searching} + {:else} + { feedback = undefined - return keypr.key === "Enter" ? performSearch() : undefined + if(keypr.key === "Enter"){ + performSearch() + keypr.preventDefault() + } + return undefined }} - on:focus={() => {isFocused.setData(true)}} - on:blur={() => {isFocused.setData(false)}} - bind:value={$searchContents} - use:placeholder={Translations.t.general.search.search} - use:ariaLabel={Translations.t.general.search.search} - /> - {#if feedback !== undefined} - - + on:focus={() => {isFocused.setData(true)}} + on:blur={() => {checkFocus()}} + bind:value={$searchContents} + use:placeholder={Translations.t.general.search.search} + use:ariaLabel={Translations.t.general.search.search} + /> + {#if feedback !== undefined} + + + {/if} {/if} - {/if} - -
+ +
- diff --git a/src/UI/BigComponents/SearchResult.svelte b/src/UI/BigComponents/SearchResult.svelte index 31094e0d7..ab010f68d 100644 --- a/src/UI/BigComponents/SearchResult.svelte +++ b/src/UI/BigComponents/SearchResult.svelte @@ -16,14 +16,17 @@ import Tr from "../Base/Tr.svelte" import { Translation } from "../i18n/Translation" import MoreScreen from "./MoreScreen" + import TagRenderingConfig from "../../Models/ThemeConfig/TagRenderingConfig" export let entry: GeoCodeResult export let state: SpecialVisualizationState let layer: LayerConfig let tags: UIEventSource> + let descriptionTr: TagRenderingConfig = undefined if (entry.feature?.properties?.id) { layer = state.layout.getMatchingLayer(entry.feature.properties) tags = state.featureProperties.getStore(entry.feature.properties.id) + descriptionTr = layer.tagRenderings.find(tr => tr.labels.indexOf("description") >= 0) } let dispatch = createEventDispatcher<{ select }>() @@ -35,7 +38,6 @@ let otherTheme: MinimalLayoutInformation | undefined = entry.payload function select() { - console.log("Selected search entry", entry) if (entry.boundingbox) { const [lat0, lat1, lon0, lon1] = entry.boundingbox state.mapProperties.bounds.set( @@ -56,7 +58,8 @@ {#if otherTheme} - +
@@ -68,7 +71,7 @@ {:else} -
diff --git a/src/UI/BigComponents/SearchResults.svelte b/src/UI/BigComponents/SearchResults.svelte index b9bea940c..5a54d4699 100644 --- a/src/UI/BigComponents/SearchResults.svelte +++ b/src/UI/BigComponents/SearchResults.svelte @@ -2,15 +2,15 @@ import type { GeoCodeResult } from "../../Logic/Geocoding/GeocodingProvider" import SearchResult from "./SearchResult.svelte" import type { SpecialVisualizationState } from "../SpecialVisualization" - import { XMarkIcon } from "@babeard/svelte-heroicons/solid" import { Store, UIEventSource } from "../../Logic/UIEventSource" import Loading from "../Base/Loading.svelte" import Tr from "../Base/Tr.svelte" import Translations from "../i18n/Translations" import MoreScreen from "./MoreScreen" + import { XCircleIcon } from "@babeard/svelte-heroicons/solid" export let state: SpecialVisualizationState - export let results: { success: GeoCodeResult[] } | { error } + export let results: GeoCodeResult[] export let searchTerm: Store export let isFocused: UIEventSource @@ -19,63 +19,59 @@ let allowOtherThemes = state.featureSwitches.featureSwitchBackToThemeOverview -
- {#if results?.["error"] !== undefined} - +
+ - {:else if $searchTerm.length > 0 && results === undefined} - - {:else if results?.["success"]?.length > 0} -
-